home *** CD-ROM | disk | FTP | other *** search
/ Aminet 43 / Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso / Aminet / comm / mail / YAM22src.lha / YAM_RE.c < prev    next >
C/C++ Source or Header  |  2000-11-03  |  108KB  |  2,542 lines

  1. /***************************************************************************
  2.  
  3.  YAM - Yet Another Mailer
  4.  Copyright (C) 2000  Marcel Beck <mbeck@yam.ch>
  5.  
  6.  This program is free software; you can redistribute it and/or modify
  7.  it under the terms of the GNU General Public License as published by
  8.  the Free Software Foundation; either version 2 of the License, or
  9.  (at your option) any later version.
  10.  
  11.  This program is distributed in the hope that it will be useful,
  12.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  GNU General Public License for more details.
  15.  
  16.  You should have received a copy of the GNU General Public License
  17.  along with this program; if not, write to the Free Software
  18.  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20.  YAM Official Support Site :  http://www.yam.ch
  21.  YAM OpenSource project    :  http://sourceforge.net/projects/yamos/
  22.  
  23. ***************************************************************************/
  24.  
  25. #include "YAM.h"
  26.  
  27. /***************************************************************************
  28.  Module: Read
  29. ***************************************************************************/
  30.  
  31. /// RE_GetQuestion
  32. //  Finds previous message in a thread
  33. struct Mail *RE_GetQuestion(long irtid)
  34. {
  35.    struct Folder **flist;
  36.    struct Mail *mail;
  37.    int b;
  38.    
  39.    if (irtid) if (flist = FO_CreateList())
  40.    {
  41.      for (b = 1; b <= (int)*flist; b++) if (MA_GetIndex(flist[b]))
  42.         for (mail = flist[b]->Messages; mail; mail = mail->Next)
  43.            if (mail->cMsgID) if (mail->cMsgID == irtid) { free(flist); return mail; }
  44.      free(flist);
  45.    }
  46.    return NULL;
  47. }
  48. ///
  49. /// RE_GetAnswer
  50. //  Find next message in a thread
  51. struct Mail *RE_GetAnswer(long id)
  52. {
  53.    struct Folder **flist;
  54.    struct Mail *mail;
  55.    int b;
  56.    
  57.    if (id) if (flist = FO_CreateList())
  58.    {
  59.      for (b = 1; b <= (int)*flist; b++) if (MA_GetIndex(flist[b]))
  60.          for (mail = flist[b]->Messages; mail; mail = mail->Next)
  61.             if (mail->cIRTMsgID) if (mail->cIRTMsgID == id) { free(flist); return mail; }
  62.      free(flist);
  63.    }
  64.    return NULL;
  65. }
  66. ///
  67. /// RE_Follow
  68. //  Follows a thread in either direction
  69. SAVEDS ASM void RE_Follow(REG(a1) int *arg)
  70. {  
  71.    int i, direction = arg[0], winnum = arg[1];
  72.    struct Folder **flist;
  73.    struct Mail *fmail = NULL;
  74.    BOOL allloaded = TRUE;
  75.  
  76.    if (flist = FO_CreateList())
  77.    {
  78.       for (i = 1; i < (int)*flist; i++) if (flist[i]->LoadedMode != 2 && flist[i]->Type != FT_SEPARATOR) allloaded = FALSE;
  79.       free(flist);
  80.    }
  81.    if (!allloaded) if (!MUI_Request(G->App, G->RE[winnum]->GUI.WI, 0, GetStr(MSG_MA_ConfirmReq), GetStr(MSG_YesNoReq), GetStr(MSG_RE_FollowThreadReq))) return;
  82.    if (direction == -1) fmail = RE_GetQuestion(G->RE[winnum]->Mail.cIRTMsgID);
  83.    if (direction ==  1) fmail = RE_GetAnswer  (G->RE[winnum]->Mail.cMsgID);
  84.    if (fmail)
  85.    {
  86.       struct MailInfo *mi;
  87.       int pos;
  88.       FO_GetFolderByName(fmail->Folder->Name, &pos);
  89.       set(G->MA->GUI.NL_FOLDERS, MUIA_NList_Active, pos);
  90.       mi = GetMailInfo(fmail);
  91.       set(G->MA->GUI.NL_MAILS, MUIA_NList_Active, mi->Pos);
  92.       RE_ReadMessage(winnum, fmail);
  93.    }
  94.    else DisplayBeep(0);
  95. }
  96. MakeHook(RE_FollowHook, RE_Follow);
  97. ///
  98. /// RE_SwitchMessage
  99. //  Goes to next or previous (new) message in list
  100. void RE_SwitchMessage(int winnum, int direction, BOOL onlynew)
  101. {
  102.    extern struct Hook RE_CloseHook;
  103.    struct Mail *mail = G->RE[winnum]->MailPtr;
  104.    struct MailInfo *mi = GetMailInfo(mail);
  105.    int act = mi->Pos;
  106.  
  107.    G->RE[winnum]->LastDirection = direction;
  108.    MA_ChangeFolder(mail->Folder);
  109.    for (act += direction; act >= 0; act += direction)
  110.    {
  111.       DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_GetEntry, act, &mail);
  112.       if (!mail) break;
  113.       if (!onlynew || (mail->Status == STATUS_NEW || mail->Status == STATUS_UNR))
  114.       {
  115.          set(G->MA->GUI.NL_MAILS, MUIA_NList_Active, act);
  116.          RE_ReadMessage(winnum, mail);
  117.          return;
  118.       }
  119.    }
  120.    if (onlynew) DisplayBeep(0); else DoMethod(G->App, MUIM_CallHook, &RE_CloseHook, winnum);
  121. }
  122. ///
  123. /// RE_PrevNext
  124. //  Goes to next or previous (new) message in list
  125. SAVEDS ASM void RE_PrevNext(REG(a1) int *arg)
  126. {  
  127.    BOOL onlynew = arg[1] & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT);
  128.    if (arg[3]) return; // Toolbar qualifier bug work-around
  129.    RE_SwitchMessage(arg[2], arg[0], onlynew);
  130. }
  131. MakeHook(RE_PrevNextHook, RE_PrevNext);
  132. ///
  133. /// RE_PrevNextPageFunc
  134. //  Flips one page back or forth
  135. SAVEDS ASM void RE_PrevNextPageFunc(REG(a1) int *arg)
  136. {
  137.    int direct = arg[0], winnum = arg[1], visible;
  138.    struct RE_GUIData *gui = &G->RE[winnum]->GUI;
  139.    get(gui->SL_TEXT, MUIA_Prop_Visible, &visible);
  140.    DoMethod(gui->SL_TEXT, MUIM_Numeric_Increase, visible*direct);
  141. }
  142. MakeHook(RE_PrevNextPageHook, RE_PrevNextPageFunc);
  143. ///
  144. /// RE_UpdateDisplay
  145. //  Updates message display after deleting/moving the current message
  146. void RE_UpdateDisplay(int pos, int winnum)
  147. {
  148.    struct Mail *mail = NULL;
  149.  
  150.    if (G->RE[winnum]->LastDirection == -1) --pos;
  151.    if (pos >= 0)
  152.    {
  153.       DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_GetEntry, pos, &mail);
  154.       if (mail)
  155.       {
  156.          set(G->MA->GUI.NL_MAILS, MUIA_NList_Active, pos);
  157.          RE_ReadMessage(winnum, mail);
  158.          return;
  159.       }
  160.    }
  161.    DoMethod(G->App, MUIM_CallHook, &RE_CloseHook, winnum);
  162. }
  163. ///
  164. /// RE_UpdateStatusGroup
  165. //  Updates status images (right side of the toolbar)
  166. void RE_UpdateStatusGroup(int winnum)
  167. {
  168.    struct RE_ClassData *re = G->RE[winnum];
  169.    struct RE_GUIData *gui = &re->GUI;
  170.    struct Mail *mail = re->MailPtr;
  171.  
  172.    set(gui->GR_STATUS[0], MUIA_Group_ActivePage, 1+mail->Status);
  173.    set(gui->GR_STATUS[1], MUIA_Group_ActivePage, re->PGPEncrypted ? 1 : 0);
  174.    set(gui->GR_STATUS[2], MUIA_Group_ActivePage, re->PGPSigned ? 1 : 0);
  175. }
  176. ///
  177. /// RE_SendMDN
  178. //  Creates a message disposition notification
  179. void RE_SendMDN(int MDNtype, struct Mail *mail, struct Person *recipient, BOOL sendnow)
  180. {
  181.    static char *MDNMessage[5] =
  182.    {
  183.       "The message written on %s to %s with subject \"%s\" has been displayed. This is no guarantee that the content has been read or understood.\n",
  184.       "The message written on %s to %s with subject \"%s\" has been sent somewhere %s, without being displayed to the user. The user may or may not see the message later.\n",
  185.       "The message written on %s to %s with subject \"%s\" has been processed %s, without being displayed to the user. The user may or may not see the message later.\n",
  186.       "The message written on %s to %s with subject \"%s\" has been deleted %s. The recipient may or may not have seen the message. The recipient may \"undelete\" the message at a later time and read the message.\n",
  187.       "%s doesn't wish to inform you about the disposition of your message written on %s with subject \"%s\".\n"
  188.    };
  189.    struct WritePart *p1 = NewPart(2), *p2, *p3;
  190.    struct TempFile *tf1, *tf2, *tf3;
  191.    char buf[SIZE_LINE], disp[SIZE_DEFAULT], *mode;
  192.    struct Compose comp;
  193.  
  194.    if (tf1 = OpenTempFile("w"))
  195.    {
  196.       char *date = DateStamp2String(&mail->Date, DSS_DATETIME), *rcpt = BuildAddrName2(&mail->To), *subj = mail->Subject;
  197.       p1->Filename = tf1->Filename;
  198.       mode = (MDNtype&MDN_AUTOACT) ? "automatically" : "in response to a user command";
  199.       strcpy(disp, (MDNtype&MDN_AUTOACT) ? "automatic-action/" : "manual-action/");
  200.       strcat(disp, (MDNtype&MDN_AUTOSEND) ? "MDN-sent-automatically; " : "MDN-sent-manually; ");
  201.       switch (MDNtype & MDN_TYPEMASK)
  202.       {
  203.          case MDN_READ: strcat(disp, "displayed");  fprintf(tf1->FP, MDNMessage[0], date, rcpt, subj); break;
  204.          case MDN_DISP: strcat(disp, "dispatched"); fprintf(tf1->FP, MDNMessage[1], date, rcpt, subj, mode); break;
  205.          case MDN_PROC: strcat(disp, "processed");  fprintf(tf1->FP, MDNMessage[2], date, rcpt, subj, mode); break;
  206.          case MDN_DELE: strcat(disp, "deleted");    fprintf(tf1->FP, MDNMessage[3], date, rcpt, subj, mode); break;
  207.          case MDN_DENY: strcat(disp, "denied");     fprintf(tf1->FP, MDNMessage[4], rcpt, date, subj); break;
  208.       }
  209.       fclose(tf1->FP); tf1->FP = NULL;
  210.       SimpleWordWrap(tf1->Filename, 72);
  211.       p2 = p1->Next = NewPart(2);
  212.       if (tf2 = OpenTempFile("w"))
  213.       {
  214.          char mfile[SIZE_MFILE];
  215.          struct Folder *outfolder = FO_GetFolderByType(FT_OUTGOING, NULL);
  216.          struct ExtendedMail *email = MA_ExamineMail(mail->Folder, mail->MailFile, "", TRUE);
  217.          p2->ContentType = "message/disposition-notification";
  218.          p2->Filename = tf2->Filename;
  219.          sprintf(buf, "%s (YAM %s)", C->SMTP_Domain, __VERSION__);
  220.          EmitHeader(tf2->FP, "Reporting-UA", buf);
  221.          if (*email->OriginalRcpt.Address)
  222.          {
  223.             sprintf(buf, "rfc822;%s", BuildAddrName2(&email->OriginalRcpt));
  224.             EmitHeader(tf2->FP, "Original-Recipient", buf);
  225.          }
  226.          sprintf(buf, "rfc822;%s", BuildAddrName(C->EmailAddress, C->RealName));
  227.          EmitHeader(tf2->FP, "Final-Recipient", buf);
  228.          EmitHeader(tf2->FP, "Original-Message-ID", email->MsgID);
  229.          EmitHeader(tf2->FP, "Disposition", disp);
  230.          fclose(tf2->FP);  tf2->FP = NULL;
  231.          p3 = p2->Next = NewPart(2);
  232.          if (tf3 = OpenTempFile("w"))
  233.          {
  234.             char fullfile[SIZE_PATHFILE];
  235.             FILE *fh;
  236.             p3->ContentType = "text/rfc822-headers";
  237.             p3->Filename = tf3->Filename;
  238.             if (StartUnpack(GetMailFile(NULL, mail->Folder, mail), fullfile, mail->Folder))
  239.             {
  240.                if (fh = fopen(fullfile, "r"))
  241.                {
  242.                   while (fgets(buf, SIZE_LINE, fh)) if (*buf == '\n') break; else fputs(buf, tf3->FP);
  243.                   fclose(fh);
  244.                }
  245.                FinishUnpack(fullfile);
  246.             }
  247.             fclose(tf3->FP); tf3->FP = NULL;
  248.             clear(&comp, sizeof(struct Compose));
  249.             comp.MailTo = StrBufCpy(comp.MailTo, BuildAddrName2(recipient));
  250.             comp.Subject = "Disposition Notification";
  251.             comp.ReportType = 1;
  252.             comp.FirstPart = p1;
  253.             if (comp.FH = fopen(MA_NewMailFile(outfolder, mfile, 0), "w"))
  254.             {
  255.                struct Mail *mlist[3];
  256.                mlist[0] = (struct Mail *)1; mlist[2] = NULL;
  257.                WriteOutMessage(&comp);
  258.                fclose(comp.FH);
  259.                if (email = MA_ExamineMail(outfolder, mfile, Status[STATUS_WFS], TRUE))
  260.                {
  261.                   mlist[2] = AddMailToList((struct Mail *)email, outfolder);
  262.                   MA_FreeEMailStruct(email);
  263.                }
  264.                if (sendnow && mlist[2] && !G->TR) MA_SendMList(mlist);
  265.             }
  266.             else ER_NewError(GetStr(MSG_ER_CreateMailError), NULL, NULL);
  267.             FreeStrBuf(comp.MailTo);
  268.             CloseTempFile(tf3);
  269.          }
  270.          MA_FreeEMailStruct(email);
  271.          CloseTempFile(tf2);
  272.       }
  273.       CloseTempFile(tf1);
  274.    }
  275.    FreePartsList(p1);
  276. }
  277. ///
  278. /// RE_DoMDN
  279. //  Handles message disposition requests
  280. void RE_DoMDN(int MDNtype, struct Mail *mail)
  281. {
  282.    int MDNmode;
  283.    switch (MDNtype)
  284.    {
  285.       case MDN_READ: MDNmode = C->MDN_Display; break;
  286.       case MDN_PROC:
  287.       case MDN_DISP: MDNmode = C->MDN_Process; break;
  288.       case MDN_DELE: MDNmode = C->MDN_Delete; break;
  289.       default:       MDNmode = C->MDN_Filter; break;
  290.    }
  291.    if (MDNmode)
  292.    {
  293.       struct ExtendedMail *email = MA_ExamineMail(mail->Folder, mail->MailFile, NULL, TRUE);
  294.       if (*email->ReceiptTo.Address)
  295.       {
  296.          char buttons[SIZE_DEFAULT*2];
  297.          BOOL isonline = TR_IsOnline(), sendnow = C->SendMDNAtOnce && isonline;
  298.          switch (MDNmode)
  299.          {
  300.             case 1: MDNtype = MDN_DENY|MDN_AUTOSEND|MDN_AUTOACT; break;
  301.             case 2: sendnow = FALSE;
  302.                     strcpy(buttons, GetStr(MSG_RE_MDNGads1));
  303.                     if (isonline) strcat(buttons, GetStr(MSG_RE_MDNGads2));
  304.                     strcat(buttons, GetStr(MSG_RE_MDNGads3));
  305.                     switch (MUI_Request(G->App, G->MA->GUI.WI, 0, GetStr(MSG_MA_ConfirmReq), buttons, GetStr(MSG_RE_MDNReq)))
  306.                     {
  307.                        case 0: MDNtype = MDN_IGNORE; break;
  308.                        case 3: sendnow = TRUE;
  309.                        case 1: break;
  310.                        case 4: sendnow = TRUE;
  311.                        case 2: MDNtype = MDN_DENY; break;
  312.                     }
  313.                     break;
  314.             case 3: if (MDNtype != MDN_IGNORE) MDNtype |= MDN_AUTOSEND; break;
  315.          }
  316.          if (MDNtype != MDN_IGNORE) RE_SendMDN(MDNtype, mail, &email->ReceiptTo, sendnow);
  317.       }
  318.       MA_FreeEMailStruct(email);
  319.    }
  320. }
  321. ///
  322. /// RE_ReadMessage
  323. //  Displays a message in the read window
  324. void RE_ReadMessage(int winnum, struct Mail *mail)
  325. {
  326.    extern struct Hook RE_CheckSignatureHook;
  327.    struct MailInfo *mi = GetMailInfo(mail);
  328.    struct RE_ClassData *re = G->RE[winnum];
  329.    struct RE_GUIData *gui = &re->GUI;
  330.    int i;
  331.    struct Folder **flist, *folder = mail->Folder;
  332.    BOOL real = !Virtual(mail);
  333.    BOOL out = real ? OUTGOING(folder->Type) : FALSE, allloaded = TRUE;
  334.  
  335.    re->Mail = *mail;
  336.    re->MailPtr = mail;
  337.    re->PGPKey = FALSE;
  338.    re->PGPSigned = re->PGPEncrypted = 0;
  339.    sprintf(re->WTitle, "%s %s %s: ", mail->MailFile, out ? GetStr(MSG_To) : GetStr(MSG_From), out ? AddrName(mail->To) : AddrName(mail->From));
  340.    stccat(re->WTitle, mail->Subject, SIZE_DEFAULT);
  341.    set(gui->WI, MUIA_Window_Title, re->WTitle);
  342.    set(gui->MI_EDIT, MUIA_Menuitem_Enabled, out);
  343.    DoMethod(gui->TE_TEXT, MUIM_TextEditor_ClearText);
  344.    if (gui->TO_TOOLBAR)
  345.    {
  346.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 0, MUIV_Toolbar_Set_Ghosted, real ? mi->Pos == 0 : TRUE);
  347.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 1, MUIV_Toolbar_Set_Ghosted, real ? mi->Pos == folder->Total-1 : TRUE);
  348.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 9, MUIV_Toolbar_Set_Ghosted, !real);
  349.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set,10, MUIV_Toolbar_Set_Ghosted, !real);
  350.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set,11, MUIV_Toolbar_Set_Ghosted, out);
  351.    }
  352.    if (real)
  353.    {
  354.       if (flist = FO_CreateList())
  355.       {
  356.          for (i = 1; i <= (int)*flist; i++) if (flist[i]->LoadedMode != 2 && flist[i]->Type != FT_SEPARATOR) allloaded = FALSE;
  357.          free(flist);
  358.       }
  359.       if (allloaded && gui->TO_TOOLBAR)
  360.       {
  361.          DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 2, MUIV_Toolbar_Set_Ghosted, !RE_GetQuestion(mail->cIRTMsgID));
  362.          DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 3, MUIV_Toolbar_Set_Ghosted, !RE_GetAnswer(mail->cMsgID));
  363.       }
  364.    }
  365.    else if (gui->TO_TOOLBAR)
  366.    {
  367.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 2, MUIV_Toolbar_Set_Ghosted, TRUE);
  368.       DoMethod(gui->TO_TOOLBAR, MUIM_Toolbar_Set, 3, MUIV_Toolbar_Set_Ghosted, TRUE);
  369.    }
  370.    GetMailFile(G->RE[winnum]->File, folder, mail);
  371.    if (RE_LoadMessage(winnum, PM_ALL))
  372.    {
  373.       RE_DisplayMessage(winnum);
  374.       set(gui->MI_EXTKEY, MUIA_Menuitem_Enabled, re->PGPKey);
  375.       set(gui->MI_CHKSIG, MUIA_Menuitem_Enabled, re->PGPSigned > 0);
  376.       set(gui->MI_SAVEDEC, MUIA_Menuitem_Enabled, real && (re->PGPEncrypted&PGPE_MIME) > 0);
  377.       RE_UpdateStatusGroup(winnum);
  378.       MA_StartMacro(MACRO_READ, itoa(winnum));
  379.       if (real && (mail->Status == STATUS_NEW || mail->Status == STATUS_UNR))
  380.       {
  381.          MA_SetMailStatus(mail, STATUS_OLD);
  382.          DisplayStatistics(folder);
  383.          if (re->PGPSigned) DoMethod(G->App, MUIM_CallHook, &RE_CheckSignatureHook, FALSE, winnum);
  384.          RE_DoMDN(MDN_READ, mail);
  385.       }
  386.    }
  387.    else
  388.    {
  389.       RE_CleanupMessage(winnum);
  390.       DisposeModulePush(&G->RE[winnum]);
  391.    }
  392. }
  393. ///
  394. /// RE_SaveDisplay
  395. //  Saves current message as displayed
  396. void RE_SaveDisplay(int winnum, FILE *fh)
  397. {
  398.    char *ptr;
  399.  
  400.    if (G->RE[winnum]->Header)
  401.    {
  402.       int i;
  403.       fputs("\033[3m", fh);
  404.       for (i = 0; ; i++)
  405.       {
  406.          DoMethod(G->RE[winnum]->GUI.LV_HEAD, MUIM_NList_GetEntry, i, &ptr);
  407.          if (!ptr) break;
  408.          if (!strcmp(ptr, MUIX_I)) ptr += strlen(MUIX_I);
  409.          fputs(ptr, fh); fputc('\n', fh);
  410.       }
  411.       fputs("\033[23m\n", fh);
  412.    }
  413.    for (ptr = (char *)DoMethod(G->RE[winnum]->GUI.TE_TEXT, MUIM_TextEditor_ExportText); *ptr; ptr++)
  414.       if (*ptr == '\033')
  415.       {
  416.          switch (*++ptr)
  417.          {
  418.             case 'u': fputs("\033[4m", fh); break;
  419.             case 'b': fputs("\033[1m", fh); break;
  420.             case 'i': fputs("\033[3m", fh); break;
  421.             case 'n': fputs("\033[0m", fh); break;
  422.             case 'h': break;
  423.             case '[': if (!strncmp(ptr, "[s:18]", 6))     { fputs("===========================================================", fh); }
  424.                       else if (!strncmp(ptr, "[s:2]", 5)) { fputs("-----------------------------------------------------------", fh); }
  425.             case 'p': while (*ptr != ']' && *ptr && *ptr != '\n') ptr++; break;
  426.          }
  427.       }
  428.       else fputc(*ptr, fh);
  429. }  
  430. ///
  431. /// RE_SuggestName
  432. //  Suggests a file name based on the message subject
  433. char *RE_SuggestName(struct Mail *mail)
  434. {
  435.    static char name[SIZE_FILE];
  436.    char *ptr = mail->Subject;
  437.    int i = 0;
  438.  
  439.    clear(name, SIZE_FILE);
  440.    while (*ptr && i < 26)
  441.    {
  442.       if ((int)*ptr <= ' ') name[i++] = '_';
  443.       else if (*ptr != ':' && *ptr != '/') name[i++] = *ptr;
  444.       ptr++;
  445.    }
  446.    strcat(name, ".msg");
  447.    return name;
  448. }
  449. ///
  450. /// RE_Export
  451. //  Saves message or attachments to disk
  452. BOOL RE_Export(int winnum, char *source, char *dest, char *name, int nr, BOOL force, BOOL overwrite, char *ctype)
  453. {
  454.    char buffer[SIZE_PATHFILE], buffer2[SIZE_FILE+SIZE_DEFAULT];
  455.    APTR win = winnum==4 ? G->MA->GUI.WI : G->RE[winnum]->GUI.WI;
  456.    struct Mail *mail = &G->RE[winnum]->Mail;
  457.  
  458.    if (!*dest)
  459.    {
  460.       if (*name) strcpy(buffer2, name);
  461.       else if (nr) sprintf(buffer2, "%s-%ld", G->RE[winnum]->Mail.MailFile, nr);
  462.       else strcpy(buffer2, RE_SuggestName(&(G->RE[winnum]->Mail)));
  463.       if (force) strmfp(dest = buffer, C->DetachDir, buffer2);
  464.       else if (ReqFile(ASL_DETACH, win, GetStr(MSG_RE_SaveMessage), 1, C->DetachDir, buffer2))
  465.          strmfp(dest = buffer, G->ASLReq[ASL_DETACH]->fr_Drawer, G->ASLReq[ASL_DETACH]->fr_File);
  466.       else return FALSE;
  467.    }
  468.    if (FileExists(dest) && !overwrite)
  469.    {
  470.       sprintf(buffer2, GetStr(MSG_RE_Overwrite), FilePart(dest));
  471.       if (!MUI_Request(G->App, win, 0, GetStr(MSG_MA_ConfirmReq), GetStr(MSG_OkayCancelReq), buffer2)) return FALSE;
  472.    }
  473.    if (!CopyFile(dest, 0, source, 0))
  474.    {
  475.       ER_NewError(GetStr(MSG_ER_CantCreateFile), dest, NULL);
  476.       return FALSE;
  477.    }
  478.    SetComment(dest, BuildAddrName2(&mail->From));
  479.    if (!stricmp(ctype, ContType[CT_AP_AEXE])) SetProtection(dest, 0);
  480.    if (!stricmp(ctype, ContType[CT_AP_SCRIPT])) SetProtection(dest, FIBF_SCRIPT);
  481.    AppendLogVerbose(80, GetStr(MSG_LOG_SavingAtt), dest, mail->MailFile, FolderName(mail->Folder), "");
  482.    return TRUE;
  483. }
  484. ///
  485. /// RE_MoveFunc
  486. //  Moves the current message to another folder
  487. SAVEDS ASM void RE_MoveFunc(REG(a1) int *arg)
  488. {
  489.    int winnum = *arg;
  490.    struct Folder *srcfolder = G->RE[winnum]->Mail.Folder;
  491.    struct Mail *mail = G->RE[winnum]->MailPtr;
  492.    if (MailExists(mail, srcfolder))
  493.    {
  494.       int pos;
  495.       struct Folder *dstfolder = FolderRequest(GetStr(MSG_MA_MoveMsg), GetStr(MSG_MA_MoveMsgReq), GetStr(MSG_MA_MoveGad), GetStr(MSG_Cancel), srcfolder, G->RE[winnum]->GUI.WI);
  496.       if (dstfolder) if ((pos = SelectMessage(mail)) >= 0)
  497.       {
  498.          MA_MoveCopy(mail, srcfolder, dstfolder, FALSE);
  499.          RE_UpdateDisplay(pos, winnum);
  500.          AppendLogNormal(22, GetStr(MSG_LOG_Moving), (void *)1, srcfolder->Name, dstfolder->Name, "");
  501.       }
  502.    }
  503. }
  504. MakeHook(RE_MoveHook, RE_MoveFunc);
  505. ///
  506. /// RE_CopyFunc
  507. //  Copies the current message to another folder
  508. SAVEDS ASM void RE_CopyFunc(REG(a1) int *arg)
  509. {
  510.    int winnum = *arg;
  511.    struct Folder *srcfolder = G->RE[winnum]->Mail.Folder;
  512.    struct Mail *mail = G->RE[winnum]->MailPtr;
  513.    if (MailExists(mail, srcfolder))
  514.    {
  515.       struct Folder *dstfolder = FolderRequest(GetStr(MSG_MA_CopyMsg), GetStr(MSG_MA_MoveMsgReq), GetStr(MSG_MA_CopyGad), GetStr(MSG_Cancel), NULL, G->RE[winnum]->GUI.WI);
  516.       if (dstfolder)
  517.          if (srcfolder)
  518.          {
  519.             MA_MoveCopy(mail, srcfolder, dstfolder, TRUE);
  520.             AppendLogNormal(24, GetStr(MSG_LOG_Copying), (void *)1, srcfolder->Name, dstfolder->Name, "");
  521.          }
  522.          else if (RE_Export(winnum, G->RE[winnum]->File, MA_NewMailFile(dstfolder, mail->MailFile, 0), "", 0, FALSE, FALSE, ContType[CT_ME_EMAIL]))
  523.          {
  524.             APTR lv;
  525.             struct Mail *newmail = AddMailToList(mail, dstfolder);
  526.             if (lv = WhichLV(dstfolder)) DoMethod(lv, MUIM_NList_InsertSingle, newmail, MUIV_NList_Insert_Sorted);
  527.             MA_SetMailStatus(newmail, STATUS_OLD);
  528.          }
  529.    }
  530. }
  531. MakeHook(RE_CopyHook, RE_CopyFunc);
  532. ///
  533. /// RE_DeleteFunc
  534. //  Deletes the current message
  535. SAVEDS ASM void RE_DeleteFunc(REG(a1) int *arg)
  536. {
  537.    BOOL delatonce = arg[0] & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT);
  538.    int pos, winnum = arg[1];
  539.    struct Folder *folder = G->RE[winnum]->Mail.Folder, *delfolder = FO_GetFolderByType(FT_DELETED, NULL);
  540.    struct Mail *mail = G->RE[winnum]->MailPtr;
  541.    if (arg[2]) return; // Toolbar qualifier bug work-around
  542.    if (MailExists(mail, folder)) if ((pos = SelectMessage(mail)) >= 0)
  543.    {
  544.       MA_DeleteSingle(G->RE[winnum]->MailPtr, delatonce);
  545.       RE_UpdateDisplay(pos, winnum);
  546.       if (delatonce) AppendLogNormal(20, GetStr(MSG_LOG_Deleting), (void *)1, folder->Name, "", "");
  547.       else           AppendLogNormal(22, GetStr(MSG_LOG_Moving), (void *)1, folder->Name, delfolder->Name, "");
  548.    }
  549. }
  550. MakeHook(RE_DeleteHook, RE_DeleteFunc);
  551. ///
  552. /// RE_PrintFunc
  553. //  Sends the current message or an attachment to the printer
  554. SAVEDS ASM void RE_PrintFunc(REG(a1) int *arg)
  555. {
  556.    int winnum = *arg;
  557.    struct Part *part;
  558.    FILE *prt;
  559.  
  560.    if (part = AttachRequest(GetStr(MSG_RE_PrintMsg), GetStr(MSG_RE_SelectPrintPart), GetStr(MSG_RE_PrintGad), GetStr(MSG_Cancel), winnum, ATTREQ_PRINT|ATTREQ_MULTI, G->RE[winnum]->GUI.WI))
  561.    {
  562.       if (C->PrinterCheck) if (!CheckPrinter()) return;
  563.       Busy(GetStr(MSG_BusyDecPrinting), "", 0, 0);
  564.       for (; part; part = part->NextSelected) switch (part->Nr)
  565.       {
  566.          case -2: CopyFile("PRT:", 0, G->RE[winnum]->File, 0);
  567.                   break;
  568.          case -1: if (prt = fopen("PRT:","w"))
  569.                   {
  570.                      RE_SaveDisplay(winnum, prt);
  571.                      fclose(prt);
  572.                   }
  573.                   break;
  574.          default: CopyFile("PRT:", 0, part->Filename, 0);
  575.       }
  576.       BusyEnd;
  577.    }
  578. }
  579. MakeHook(RE_PrintHook, RE_PrintFunc);
  580. ///
  581. /// RE_SaveFunc
  582. //  Saves the current message or an attachment to disk
  583. SAVEDS ASM void RE_SaveFunc(REG(a1) int *arg)
  584. {
  585.    int winnum = *arg;
  586.    struct Part *part;
  587.    struct TempFile *tf;
  588.  
  589.    if (part = AttachRequest(GetStr(MSG_RE_SaveMessage), GetStr(MSG_RE_SelectSavePart), GetStr(MSG_RE_SaveGad), GetStr(MSG_Cancel), winnum, ATTREQ_SAVE|ATTREQ_MULTI, G->RE[winnum]->GUI.WI))
  590.    {
  591.       Busy(GetStr(MSG_BusyDecSaving), "", 0, 0);
  592.       for (; part; part = part->NextSelected) switch (part->Nr)
  593.       {
  594.          case -2: RE_Export(winnum, G->RE[winnum]->File, "", "", 0, FALSE, FALSE, ContType[CT_ME_EMAIL]);
  595.                   break;
  596.          case -1: if (tf = OpenTempFile("w"))
  597.                   {
  598.                      RE_SaveDisplay(winnum, tf->FP);
  599.                      fclose(tf->FP); tf->FP = NULL;
  600.                      RE_Export(winnum, tf->Filename, "", "", 0, FALSE, FALSE, ContType[CT_TX_PLAIN]);
  601.                      CloseTempFile(tf);
  602.                   }
  603.                   break;
  604.          default: RE_DecodePart(part);
  605.                   RE_Export(winnum, part->Filename, "", part->Name, part->Nr, FALSE, FALSE, part->ContentType);
  606.       }
  607.       BusyEnd;
  608.    }
  609. }
  610. MakeHook(RE_SaveHook, RE_SaveFunc);
  611. ///
  612. /// RE_DisplayMIME
  613. //  Displays a message part (attachment) using a MIME viewer
  614. void RE_DisplayMIME(char *fname, char *ctype)
  615. {
  616.    static char command[SIZE_COMMAND+SIZE_PATHFILE];
  617.    int i;
  618.    struct MimeView *mv = NULL;
  619.  
  620.    for (i = 1; i < MAXMV; i++) if (C->MV[i])
  621.       if (MatchNoCase(ctype, C->MV[i]->ContentType)) { mv = C->MV[i]; break; }
  622.    if (!mv && !stricmp(ctype, "message/rfc822"))
  623.    {
  624.       int winnum;
  625.       struct Mail *mail;
  626.       struct ExtendedMail *email;
  627.       struct TempFile *tf = OpenTempFile(NULL);
  628.       CopyFile(tf->Filename, NULL, fname, NULL);
  629.       if (email = MA_ExamineMail(NULL, FilePart(tf->Filename), "O", TRUE))
  630.       {
  631.          mail = malloc(sizeof(struct Mail));
  632.          memcpy(mail, &email->Mail, sizeof(struct Mail));
  633.          mail->Folder = NULL;
  634.          mail->Flags |= MFLAG_NOFOLDER;
  635.          MA_FreeEMailStruct(email);
  636.          if ((winnum = RE_Open(-1, FALSE)) != -1)
  637.          {
  638.             G->RE[winnum]->TempFile = tf;
  639.             if (SafeOpenWindow(G->RE[winnum]->GUI.WI)) RE_ReadMessage(winnum, mail);
  640.             else DisposeModulePush(&G->RE[winnum]);
  641.          }
  642.       }
  643.    }
  644.    else
  645.    {
  646.       if (!mv)
  647.       {
  648.          if (C->IdentifyBin)
  649.          {
  650.             ctype = IdentifyFile(fname);
  651.             for (i = 1; i < MAXMV; i++) if (C->MV[i])
  652.                if (MatchNoCase(ctype, C->MV[i]->ContentType)) { mv = C->MV[i]; break; }
  653.          }
  654.          if (!mv) mv = C->MV[0];
  655.       }
  656.       sprintf(command, mv->Command, fname);
  657.       ExecuteCommand(command, TRUE, OUT_NIL);
  658.    }
  659. }
  660. ///
  661. /// RE_DisplayFunc
  662. //  Shows message or attachments separately
  663. SAVEDS ASM void RE_DisplayFunc(REG(a1) int *arg)
  664. {
  665.    int winnum = *arg;
  666.    struct Part *part;
  667.  
  668.    if (part = AttachRequest(GetStr(MSG_RE_DisplayMsg), GetStr(MSG_RE_SelectDisplayPart), GetStr(MSG_RE_DisplayGad), GetStr(MSG_Cancel), winnum, ATTREQ_DISP|ATTREQ_MULTI, G->RE[winnum]->GUI.WI))
  669.    {
  670.       Busy(GetStr(MSG_BusyDecDisplaying), "", 0, 0);
  671.       for (; part; part = part->NextSelected)
  672.       {
  673.          RE_DecodePart(part);
  674.          if (part->Nr == -2) RE_DisplayMIME(G->RE[winnum]->File, "text/plain");
  675.          else RE_DisplayMIME(part->Filename, part->ContentType);
  676.       }
  677.       BusyEnd;
  678.    }
  679. }
  680. MakeHook(RE_DisplayHook, RE_DisplayFunc);
  681. ///
  682. /// RE_SaveAll
  683. //  Saves all attachments to disk
  684. void RE_SaveAll(int winnum, char *path)
  685. {
  686.    struct Part *part;
  687.    char dest[SIZE_PATHFILE], fname[SIZE_FILE];
  688.  
  689.    for (part = G->RE[winnum]->FirstPart->Next->Next; part; part = part->Next)
  690.    {
  691.       if (*part->Name) stccpy(fname, part->Name, SIZE_FILE);
  692.       else sprintf(fname, "%s-%ld", G->RE[winnum]->Mail.MailFile, part->Nr);
  693.       strmfp(dest, path, fname);
  694.       RE_DecodePart(part);
  695.       RE_Export(winnum, part->Filename, dest, part->Name, part->Nr, FALSE, FALSE, part->ContentType);
  696.    }
  697. }
  698. ///
  699. /// RE_SaveAllFunc
  700. //  Asks user for a directory and saves all attachments there
  701. SAVEDS ASM void RE_SaveAllFunc(REG(a1) int *arg)
  702. {
  703.    struct Part *part = G->RE[*arg]->FirstPart->Next;
  704.    if (part) if (part->Next) if (ReqFile(ASL_DETACH, G->RE[*arg]->GUI.WI, GetStr(MSG_RE_SaveMessage), 5, C->DetachDir, ""))
  705.    {
  706.       Busy(GetStr(MSG_BusyDecSaving), "", 0, 0);
  707.       RE_SaveAll(*arg, G->ASLReq[ASL_DETACH]->fr_Drawer);
  708.       BusyEnd;
  709.    }
  710. }
  711. MakeHook(RE_SaveAllHook, RE_SaveAllFunc);
  712. ///
  713. /// RE_RemoveAttachFunc
  714. //  Removes attachments from the current message
  715. SAVEDS ASM void RE_RemoveAttachFunc(REG(a1) int *arg)
  716. {
  717.    struct Mail *mail = G->RE[*arg]->MailPtr;
  718.    struct MailInfo *mi;
  719.    MA_RemoveAttach(mail);
  720.    if ((mi = GetMailInfo(mail))->Pos >= 0)
  721.    {
  722.       DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_Redraw, mi->Pos);
  723.       MA_ChangeSelectedFunc();
  724.       DisplayStatistics(mail->Folder);
  725.    }
  726.    RE_ReadMessage(*arg, mail);
  727. }
  728. MakeHook(RE_RemoveAttachHook, RE_RemoveAttachFunc);
  729. ///
  730. /// RE_NewFunc
  731. //  Starts a new message based on the current one
  732. SAVEDS ASM void RE_NewFunc(REG(a1) int *arg)
  733. {
  734.    int mode = arg[0], winnum = arg[2], flags = 0;
  735.    ULONG qual = arg[1];
  736.    struct Mail *mail = G->RE[winnum]->MailPtr, *mlist[3] = { (struct Mail *)1, NULL, NULL };
  737.    if (arg[3]) return; // Toolbar qualifier bug work-around
  738.    if (mode == NEW_FORWARD && qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) mode = NEW_BOUNCE;
  739.    if (mode == NEW_FORWARD && qual & IEQUALIFIER_CONTROL) flags = NEWF_FWD_NOATTACH;
  740.    if (mode == NEW_REPLY && qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) flags = NEWF_REP_PRIVATE;
  741.    if (mode == NEW_REPLY && qual & (IEQUALIFIER_LALT|IEQUALIFIER_RALT)) flags = NEWF_REP_MLIST;
  742.    if (mode == NEW_REPLY && qual & IEQUALIFIER_CONTROL) flags = NEWF_REP_NOQUOTE;
  743.    mlist[2] = mail;
  744.    if (MailExists(mail, NULL)) switch (mode)
  745.    {
  746.       case NEW_NEW:     MA_NewNew(mail, flags); break;
  747.       case NEW_EDIT:    MA_NewEdit(mail, flags); break;
  748.       case NEW_BOUNCE:  MA_NewBounce(mail, flags); break;
  749.       case NEW_FORWARD: MA_NewForward(mlist, flags); break;
  750.       case NEW_REPLY:   MA_NewReply(mlist, flags); break;
  751.    }
  752. }
  753. MakeHook(RE_NewHook, RE_NewFunc);
  754. ///
  755. /// RE_GetAddressFunc
  756. //  Stores sender address of current message in the address book
  757. SAVEDS ASM void RE_GetAddressFunc(REG(a1) int *arg)
  758. {
  759.    int winnum = *arg;
  760.    struct Folder *folder = G->RE[winnum]->Mail.Folder;
  761.    struct Mail *mail = G->RE[winnum]->MailPtr, *mlist[3] = { (struct Mail *)1, NULL, NULL };
  762.    mlist[2] = mail;
  763.    if (MailExists(mail, folder)) MA_GetAddress(mlist);
  764. }
  765. MakeHook(RE_GetAddressHook, RE_GetAddressFunc);
  766. ///
  767. /// RE_ChangeSubjectFunc
  768. //  Changes the subject of the current message
  769. SAVEDS ASM void RE_ChangeSubjectFunc(REG(a1) int *arg)
  770. {
  771.    char subj[SIZE_SUBJECT];
  772.    int winnum = *arg;
  773.    struct Folder *folder = G->RE[winnum]->Mail.Folder;
  774.    struct Mail *mail = G->RE[winnum]->MailPtr;
  775.    struct MailInfo *mi;
  776.    if (MailExists(mail, folder))
  777.    {
  778.       strcpy(subj, mail->Subject);
  779.       if (StringRequest(subj, SIZE_SUBJECT, GetStr(MSG_MA_ChangeSubj), GetStr(MSG_MA_ChangeSubjReq), GetStr(MSG_Okay), NULL, GetStr(MSG_Cancel), FALSE, G->RE[*arg]->GUI.WI))
  780.       {
  781.          MA_ChangeSubject(mail, subj);
  782.          if ((mi = GetMailInfo(mail))->Pos >= 0)
  783.          {
  784.             DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_Redraw, mi->Pos);
  785.             MA_ChangeSelectedFunc();
  786.             DisplayStatistics(mail->Folder);
  787.          }
  788.          RE_ReadMessage(*arg, mail);
  789.       }
  790.    }
  791. }
  792. MakeHook(RE_ChangeSubjectHook, RE_ChangeSubjectFunc);
  793. ///
  794. /// RE_ExtractKeyFunc
  795. //  Extracts public PGP key from the current message
  796. SAVEDS ASM void RE_ExtractKeyFunc(REG(a1) int *arg)
  797. {
  798.    char fullfile[SIZE_PATHFILE], options[SIZE_PATHFILE];
  799.    struct Mail *mail = G->RE[*arg]->MailPtr;
  800.  
  801.    if (!StartUnpack(GetMailFile(NULL, NULL, mail), fullfile, mail->Folder)) return;
  802.    sprintf(options, (G->PGPVersion == 5) ? "-a %s +batchmode=1 +force" : "-ka %s +bat +f", fullfile);
  803.    PGPCommand((G->PGPVersion == 5) ? "pgpk" : "pgp", options, 0);
  804.    FinishUnpack(fullfile);
  805. }
  806. MakeHook(RE_ExtractKeyHook, RE_ExtractKeyFunc);
  807. ///
  808. /// RE_GetAddressFromLog
  809. //  Finds e-mail address in PGP output
  810. BOOL RE_GetAddressFromLog(char *buf, char *address)
  811. {
  812.    if (buf = strchr(buf, 34))
  813.    {
  814.       stccpy(address, ++buf, SIZE_ADDRESS);
  815.       if (buf = strchr(address, 34)) *buf = 0;
  816.       return TRUE;
  817.    }
  818.    return FALSE;
  819. }
  820. ///
  821. /// RE_GetSigFromLog
  822. //  Interprets logfile created from the PGP signature check
  823. void RE_GetSigFromLog(int winnum, char *decrFor)
  824. {
  825.    BOOL sigDone = FALSE, decrFail = FALSE;
  826.    struct RE_ClassData *re = G->RE[winnum];
  827.    FILE *fh;
  828.    char buffer[SIZE_LARGE];
  829.  
  830.    if (fh = fopen(PGPLOGFILE, "r"))
  831.    {
  832.       while (GetLine(fh, buffer, SIZE_LARGE))
  833.       {
  834.          if (!decrFail && decrFor && G->PGPVersion == 5)
  835.             if (!strnicmp(buffer, "cannot decrypt", 14))
  836.             {
  837.                *decrFor = 0;
  838.                GetLine(fh, buffer, SIZE_LARGE); GetLine(fh, buffer, SIZE_LARGE);
  839.                RE_GetAddressFromLog(buffer, decrFor);
  840.                decrFail = TRUE;
  841.             }
  842.          if (!sigDone)
  843.          {
  844.             if (!strnicmp(buffer, "good signature", 14)) sigDone = TRUE;
  845.             if (!strnicmp(buffer, "bad signature", 13)) { re->PGPSigned |= PGPS_BADSIG; sigDone = TRUE; }
  846.             if (sigDone)
  847.             {
  848.                if (G->PGPVersion == 5) { GetLine(fh, buffer, SIZE_LARGE); GetLine(fh, buffer, SIZE_LARGE); }
  849.                if (RE_GetAddressFromLog(buffer, re->Signature)) re->PGPSigned |= PGPS_ADDRESS;
  850.                re->PGPSigned |= PGPS_CHECKED;
  851.             }
  852.          }
  853.       }
  854.       fclose(fh);
  855.       DeleteFile(PGPLOGFILE);
  856.    }
  857. }
  858. ///
  859. /// RE_CheckSignatureFunc
  860. //  Checks validity of a PGP signed message
  861. SAVEDS ASM void RE_CheckSignatureFunc(REG(a1) int *arg)
  862. {
  863.    struct RE_ClassData *re = G->RE[arg[1]];
  864.  
  865.    if ((re->PGPSigned & PGPS_OLD) && !(re->PGPSigned & PGPS_CHECKED))
  866.    {
  867.       int error;
  868.       char fullfile[SIZE_PATHFILE], options[SIZE_LARGE];
  869.       if (!StartUnpack(GetMailFile(NULL, NULL, re->MailPtr), fullfile, re->MailPtr->Folder)) return;
  870.       sprintf(options, (G->PGPVersion == 5) ? "%s -o %s +batchmode=1 +force +language=us" : "%s -o %s +bat +f", fullfile, "T:PGP.tmp");
  871.       error = PGPCommand((G->PGPVersion == 5) ? "pgpv": "pgp", options, NOERRORS|KEEPLOG);
  872.       FinishUnpack(fullfile);
  873.       DeleteFile("T:PGP.tmp");
  874.       if (error > 0) re->PGPSigned |= PGPS_BADSIG;
  875.       if (error >= 0) RE_GetSigFromLog(arg[1], NULL); else return;
  876.    }
  877.    if ((re->PGPSigned & PGPS_BADSIG) || arg[0])
  878.    {
  879.       char buffer[SIZE_LARGE];
  880.       strcpy(buffer, (re->PGPSigned & PGPS_BADSIG) ? GetStr(MSG_RE_BadSig) : GetStr(MSG_RE_GoodSig));
  881.       if (re->PGPSigned & PGPS_ADDRESS) { strcat(buffer, GetStr(MSG_RE_SigFrom)); strcat(buffer, re->Signature); }
  882.       MUI_Request(G->App, re->GUI.WI, 0, GetStr(MSG_RE_SigCheck), GetStr(MSG_Okay), buffer);
  883.    }
  884. }
  885. MakeHook(RE_CheckSignatureHook, RE_CheckSignatureFunc);
  886. ///
  887. /// RE_SaveDecryptedFunc
  888. //  Saves decrypted version of a PGP message
  889. SAVEDS ASM void RE_SaveDecryptedFunc(REG(a1) int *arg)
  890. {
  891.    struct RE_ClassData *re = G->RE[*arg];
  892.    struct WritePart *p1;
  893.    struct Compose comp;
  894.    int choice;
  895.    struct Folder *folder = re->MailPtr->Folder;
  896.    char mfile[SIZE_MFILE];
  897.  
  898.    if (!(choice = MUI_Request(G->App, re->GUI.WI, 0, GetStr(MSG_RE_SaveDecrypted), GetStr(MSG_RE_SaveDecGads), GetStr(MSG_RE_SaveDecReq)))) return;
  899.    clear(&comp, sizeof(struct Compose));
  900.    if (comp.FH = fopen(MA_NewMailFile(folder, mfile, 0), "w"))
  901.    {
  902.       struct ExtendedMail *email;
  903.       struct Mail *new;
  904.       comp.Mode = NEW_SAVEDEC;
  905.       comp.OrigMail = re->MailPtr;
  906.       comp.FirstPart = p1 = NewPart(2);
  907.       p1->Filename = re->FirstPart->Next->Filename;
  908.       WriteOutMessage(&comp);
  909.       FreePartsList(p1);
  910.       fclose(comp.FH);
  911.       if (email = MA_ExamineMail(folder, mfile, Status[re->MailPtr->Status], TRUE))
  912.       {
  913.          new = AddMailToList((struct Mail *)email, folder);
  914.          if (FO_GetCurrentFolder() == folder) DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_InsertSingle, new, MUIV_NList_Insert_Sorted);
  915.          MA_FreeEMailStruct(email);
  916.          if (choice == 2)
  917.          {
  918.             MA_DeleteSingle(re->MailPtr, FALSE);
  919.             RE_ReadMessage(*arg, new);
  920.          }
  921.       }
  922.       else ER_NewError(GetStr(MSG_ER_CreateMailError), NULL, NULL);
  923.    }
  924. }
  925. MakeHook(RE_SaveDecryptedHook, RE_SaveDecryptedFunc);
  926. ///
  927.  
  928. /*** MIME ***/
  929. /// StripTrailingSpace
  930. //  Strips trailing spaces from a string
  931. void StripTrailingSpace(char *s)
  932. {
  933.    char *t = &s[strlen(s)-1];
  934.    while (ISpace(*t) && t >= s) *t-- = 0;
  935. }
  936. ///
  937. /// ParamEnd
  938. //  Finds next parameter in header field
  939. char *ParamEnd(char *s)
  940. {
  941.    BOOL inquotes = FALSE;
  942.  
  943.    while (*s) 
  944.    {
  945.       if (inquotes) 
  946.       {
  947.          if (*s == '"') inquotes = FALSE; else if (*s == '\\') ++s;
  948.       } 
  949.       else if (*s == ';') return(s);
  950.       else if (*s == '"') inquotes = TRUE;
  951.       ++s;
  952.    }
  953.    return NULL;
  954. }
  955. ///
  956. /// Cleanse
  957. //  Removes trailing and leading spaces and converts string to lower case
  958. char *Cleanse(char *s)
  959. {
  960.    char *tmp, *news;
  961.    
  962.    news = s = stpblk(s);
  963.    for (tmp=s; *tmp; ++tmp) if (isupper((int)*tmp)) *tmp = tolower((int)*tmp);
  964.    while (tmp > news && *--tmp && ISpace(*tmp)) *tmp = 0;
  965.    return news;
  966. }
  967. ///
  968. /// UnquoteString
  969. //  Removes quotes from a string, skipping "escaped" quotes
  970. char *UnquoteString(char *s, BOOL new)
  971. {
  972.    char *ans, *t, *o = s;
  973.  
  974.    if (*s != '"') return s;
  975.    ans = malloc(1+strlen(s));
  976.    ++s;
  977.    t = ans;
  978.    while (*s) 
  979.    {
  980.       if (*s == '\\') *t++ = *++s;
  981.       else if (*s == '"') break;
  982.       else *t++ = *s;
  983.       ++s;
  984.    }
  985.    *t = 0;
  986.    if (new) return ans;
  987.    strcpy(o, ans);
  988.    free(ans);
  989.    return o;
  990. }
  991. ///
  992. /// RE_CharIn
  993. //  Converts character using translation table
  994. int RE_CharIn(char c, struct TranslationTable *tt)
  995. {
  996.    if (tt) if (tt->Header) return (int)tt->Table[(UBYTE)c];
  997.    return (int)c;
  998. }
  999. ///
  1000. /// RE_ProcessHeader (rec)
  1001. //  Processes MIME encoded message headers
  1002. STACKEXT void RE_ProcessHeader(char *prevcharset, char *s, BOOL ShowLeadingWhitespace, char *ptr)
  1003. {
  1004.    char *charset, *encoding, *txt, *txtend, *t;
  1005.    int ecode = ENC_NONE, CorrectedCharset = 0;
  1006.    struct TranslationTable *tt = NULL;
  1007.  
  1008.    if (MatchTT(prevcharset, G->TTin, TRUE)) tt = G->TTin;
  1009.    while (*s && (*s != '=')) 
  1010.    {
  1011.       if (*s == ' ' || *s == '\t') { if (ShowLeadingWhitespace) *ptr++ = ' '; }
  1012.       else 
  1013.       {
  1014.          if (!CorrectedCharset) { CorrectedCharset = TRUE; strcpy(prevcharset, "us-ascii"); }
  1015.          *ptr++ = (char)RE_CharIn(*s, tt);
  1016.       }
  1017.       if (!ShowLeadingWhitespace) ShowLeadingWhitespace = TRUE;
  1018.       ++s;
  1019.    }
  1020.    if (!*s) return;
  1021.    if (*(s+1) != '?') 
  1022.    {
  1023.       *ptr++ = '=';
  1024.       RE_ProcessHeader(prevcharset, ++s, True, ptr);
  1025.       return;
  1026.    }
  1027.    charset = s+2;
  1028.    encoding = strchr(charset, '?');
  1029.    if (!encoding) { *ptr++ = '='; RE_ProcessHeader(prevcharset, ++s, True, ptr); return; }
  1030.    txt = strchr(encoding+1, '?');
  1031.    if (!txt) { *ptr++ = '='; RE_ProcessHeader(prevcharset, ++s, True, ptr); return; }
  1032.    txtend = txt;
  1033.    do { txtend = strchr(txtend+1, '?'); } while(txtend && (*(txtend+1) != '='));
  1034.    if (!txtend) { *ptr++ = '='; RE_ProcessHeader(prevcharset, ++s, True, ptr); }
  1035.    *encoding = 0;
  1036.    *txt = 0;
  1037.    *txtend = 0;
  1038.    if (tolower((int)*(encoding+1)) == 'q') ecode = ENC_QP;
  1039.    else if (tolower((int)*(encoding+1)) == 'b') ecode = ENC_B64;
  1040.    else ER_NewError(GetStr(MSG_ER_UnknownHeaderEnc), encoding+1, NULL);
  1041.    if (stricmp(charset, prevcharset))
  1042.    {
  1043.       char *s2;
  1044.       strcpy(prevcharset, charset);
  1045.       for (s2 = prevcharset; *s2; ++s2)
  1046.          if (isupper((int)*s2)) *s2 = tolower((int)*s2);
  1047.       if (MatchTT(prevcharset, G->TTin, TRUE)) tt = G->TTin;
  1048.    }
  1049.    if (ecode == ENC_NONE) for (t = txt+1; *t; ++t) *ptr++ = RE_CharIn(*t, tt);
  1050.    else 
  1051.    {
  1052.       for (t = txt+1; *t; ++t) if (*t == '_') *t = ' ';
  1053.       if (ecode == ENC_B64) from64txt(txt+1, ptr, tt);
  1054.       else if (ecode == ENC_QP) fromqptxt(txt+1, ptr, tt);
  1055.       while (*ptr) ptr++;
  1056.    }
  1057.    *encoding = '?'; *txt = '?'; *txtend = '?';
  1058.    RE_ProcessHeader(prevcharset, txtend+2, TRUE, ptr);
  1059. }
  1060. ///
  1061. /// RE_ParseContentParameters
  1062. //  Parses parameters of Content-Type header field
  1063. void RE_ParseContentParameters(struct Part *rp)
  1064. {
  1065.    char *s, *t, *eq, *ct = rp->ContentType;
  1066.  
  1067.    s = strchr(ct, ';');
  1068.    if (!s) return;
  1069.    *s++ = 0;
  1070.    do {
  1071.       if (t = ParamEnd(s)) *t++ = 0;
  1072.       if (!(eq = strchr(s, '='))) rp->JunkParameter = Cleanse(s);
  1073.       else 
  1074.       {
  1075.          *eq++ = 0;
  1076.          s = Cleanse(s); eq = stpblk(eq);
  1077.          StripTrailingSpace(eq);
  1078.          UnquoteString(eq, FALSE);
  1079.          if (!stricmp(s, "name")) rp->CParName = eq;
  1080.          if (!stricmp(s, "description")) rp->CParDesc = eq;
  1081.          if (!stricmp(s, "boundary")) rp->CParBndr = eq;
  1082.          if (!stricmp(s, "protocol")) rp->CParProt = eq;
  1083.          if (!stricmp(s, "report-type")) rp->CParRType = eq;
  1084.          if (!stricmp(s, "charset")) rp->CParCSet = eq;
  1085.       }
  1086.       s = t;
  1087.    } while (t);
  1088. }
  1089. ///
  1090. /// RE_ScanHeader
  1091. //  Parses the header of the message or of a message part
  1092. BOOL RE_ScanHeader(struct Part *rp, FILE *in, FILE *out, int mode)
  1093. {
  1094.    int i;
  1095.    char *p;
  1096.  
  1097.    if (!MA_ReadHeader(in))
  1098.    {
  1099.       if (mode == 0) ER_NewError(GetStr(MSG_ER_MIMEError), NULL, NULL);
  1100.       else if (mode == 1) ER_NewError(GetStr(MSG_ER_MultipartEOF), NULL, NULL);
  1101.       return FALSE;
  1102.    }
  1103.    rp->HasHeaders = TRUE;
  1104.    for (i = 0; i < Header.Used; i++)
  1105.    {
  1106.       char *s = Header.Data[i];
  1107.       int ls = strlen(s);
  1108.       rp->MaxHeaderLen = max(ls, rp->MaxHeaderLen);
  1109.       if (out) { fputs(s, out); fputc('\n', out); }
  1110.       if (!strnicmp(s, "content-type:", 13))
  1111.       {
  1112.          rp->ContentType = StrBufCpy(rp->ContentType, p = stpblk(&s[13]));
  1113.          while (TRUE) 
  1114.          {
  1115.             if (!(p = strchr(rp->ContentType, '/'))) break;
  1116.             if (ISpace(*(p-1)))    for (--p; *p; ++p) *p = *(p+1);
  1117.             else if (ISpace(*++p)) for ( ; *p; ++p) *p = *(p+1);
  1118.             else break;
  1119.          }
  1120.          StripTrailingSpace(rp->ContentType);
  1121.          RE_ParseContentParameters(rp);
  1122.       }
  1123.       else if (!strnicmp(s, "content-transfer-encoding:", 26))
  1124.       {
  1125.          char buf[SIZE_DEFAULT];
  1126.          stccpy(p = buf, stpblk(&s[26]), SIZE_DEFAULT);
  1127.          StripTrailingSpace(p);
  1128.          if      (!stricmp(p, "base64"))           rp->EncodingCode = ENC_B64;
  1129.          else if (!stricmp(p, "quoted-printable")) rp->EncodingCode = ENC_QP;
  1130.          else if (!strnicmp(p, "x-uue", 5))        rp->EncodingCode = ENC_UUE;
  1131.          else if (stricmp(p, "none") && !stricmp(p, "8bit") && !stricmp(p, "7bit"))
  1132.             ER_NewError(GetStr(MSG_ER_UnknownEnc), p, NULL);
  1133.       } 
  1134.       else if (!strnicmp(s, "content-description:", 20))
  1135.       {
  1136.          stccpy(rp->Description, stpblk(&s[20]), SIZE_DEFAULT);
  1137.       } 
  1138.    }
  1139.    for (p = rp->ContentType; *p; ++p) if (isupper((int)*p)) *p = tolower((int)*p);
  1140.    return TRUE;
  1141. }
  1142. ///
  1143. /// RE_ConsumeRestOfPart
  1144. //  Processes body of a message part
  1145. BOOL RE_ConsumeRestOfPart(FILE *in, FILE *out, struct TranslationTable *tt, struct Part *rp)
  1146. {
  1147.    char *ptr, c = 0, buf[SIZE_LINE];
  1148.    UBYTE *p;
  1149.    int blen = 0;
  1150.  
  1151.    if (rp) blen = strlen(rp->Boundary);
  1152.    while (fgets(buf, SIZE_LINE, in))
  1153.    {
  1154.       if (rp) if (!strncmp(buf, rp->Boundary, blen))
  1155.       {
  1156.          if (buf[blen] == '\n') return FALSE;
  1157.          if (buf[blen] == '-' && buf[blen+1] == '-' && buf[blen+2] == '\n') return TRUE;
  1158.       }
  1159.       if (out)
  1160.       {
  1161.          if (c == '\n') fputc(c, out);
  1162.          ptr = &buf[strlen(buf)-1];
  1163.          if ((c = *ptr) == '\n') *ptr = 0;
  1164.          if (tt) for (p = buf; *p; ++p) *p = tt->Table[*p];
  1165.          fputs(buf, out);
  1166.       }
  1167.    }
  1168.    if (out && c == '\n') fputc(c, out);
  1169.    return TRUE;
  1170. }
  1171. ///
  1172. /// RE_DecodeStream
  1173. //  Decodes contents of a part
  1174. void RE_DecodeStream(struct Part *rp, FILE *in, FILE *out)
  1175. {
  1176.    struct TranslationTable *tt = NULL;
  1177.    if (rp->Nr == C->LetterPart && rp->Printable)
  1178.       if (!rp->CParCSet)
  1179.       {
  1180.          if (MatchTT(C->LocalCharset, G->TTin, TRUE) || MatchTT("us-ascii", G->TTin, TRUE)) tt = G->TTin;
  1181.       }
  1182.       else if (MatchTT(rp->CParCSet, G->TTin, TRUE)) tt = G->TTin;
  1183.    switch (rp->EncodingCode)
  1184.    {
  1185.       case ENC_B64:  from64  (in, out, tt, DoesNeedPortableNewlines(rp->ContentType)); break;
  1186.       case ENC_QP:   fromqp  (in, out, tt); break;
  1187.       case ENC_FORM: fromform(in, out, tt); break;
  1188.       case ENC_UUE:  fromuue (in, out); RE_ConsumeRestOfPart(in, NULL, NULL, NULL); break;
  1189.       default:       RE_ConsumeRestOfPart(in, out, tt, NULL);
  1190.    }
  1191. }
  1192. ///
  1193. /// RE_OpenNewPart
  1194. //  Adds a new entry to the message part list
  1195. FILE *RE_OpenNewPart(int winnum, struct Part **new, struct Part *prev, struct Part *first)
  1196. {
  1197.    FILE *fp;
  1198.    if ((*new) = calloc(1,sizeof(struct Part)))
  1199.    {
  1200.       char file[SIZE_FILE];
  1201.       if (prev)
  1202.       {
  1203.          (*new)->Prev = prev;
  1204.          prev->Next = *new;
  1205.          (*new)->Nr = prev->Nr+1;
  1206.       }
  1207.       (*new)->ContentType = StrBufCpy(NULL, "text/plain");
  1208.       (*new)->EncodingCode = ENC_NONE;
  1209.       if (first) if (strnicmp(first->ContentType, "multipart", 9))
  1210.       {
  1211.          (*new)->ContentType = StrBufCpy((*new)->ContentType, first->ContentType);
  1212.          (*new)->CParCSet = first->CParCSet;
  1213.          (*new)->EncodingCode = first->EncodingCode;
  1214.       }
  1215.       strcpy((*new)->Boundary, first ? first->Boundary : (prev ? prev->Boundary : ""));
  1216.       (*new)->Win = winnum;
  1217.       sprintf(file, "YAMraw-w%ldp%ld.txt", winnum, (*new)->Nr);
  1218.       strmfp((*new)->Filename, C->TempDir, file);
  1219.       if (fp = fopen((*new)->Filename, "w")) return fp;
  1220.       free(*new);
  1221.    }
  1222.    return NULL;
  1223. }
  1224. ///
  1225. /// RE_UndoPart
  1226. //  Removes an entry from the message part list
  1227. void RE_UndoPart(struct Part *rp)
  1228. {
  1229.    DeleteFile(rp->Filename);
  1230.    if (rp->Prev) rp->Prev->Next = rp->Next;
  1231.    if (rp->Next) rp->Next->Prev = rp->Prev;
  1232.    if (rp->ContentType) FreeStrBuf(rp->ContentType);
  1233.    free(rp);
  1234. }
  1235. ///
  1236. /// RE_RequiresSpecialHandling
  1237. //  Checks if part is PGP signed/encrypted or a MDN
  1238. int RE_RequiresSpecialHandling(struct Part *hrp)
  1239. {
  1240.    if (!stricmp(hrp->ContentType, "multipart/report") && !stricmp(hrp->CParRType, "disposition-notification")) return 1;
  1241.    if (!stricmp(hrp->ContentType, "multipart/signed") && !stricmp(hrp->CParProt, "application/pgp-signature")) return 2;
  1242.    if (!stricmp(hrp->ContentType, "multipart/encrypted") && !stricmp(hrp->CParProt, "application/pgp-encrypted")) return 3;
  1243.    return 0;
  1244. }
  1245. ///
  1246. /// RE_IsURLencoded
  1247. //  Checks if part contains encoded form data
  1248. BOOL RE_IsURLencoded(struct Part *rp)
  1249. {
  1250.    return (BOOL)(!stricmp(rp->ContentType, "application/x-www-form-urlencoded") ||
  1251.                  !stricmp(rp->ContentType, "application/x-url-encoded"));
  1252. }
  1253. ///
  1254. /// RE_SaveThisPart
  1255. //  Decides if the part should be kept in memory
  1256. BOOL RE_SaveThisPart(struct Part *rp)
  1257. {
  1258.    int pm = G->RE[rp->Win]->ParseMode;
  1259.    switch (pm)
  1260.    {
  1261.       case PM_ALL:   return TRUE;
  1262.       case PM_NONE:  return FALSE;
  1263.       case PM_TEXTS: return (BOOL)(!strnicmp(rp->ContentType, "text", 4) || RE_IsURLencoded(rp));
  1264.    }
  1265. }
  1266. ///
  1267. /// RE_SetPartInfo
  1268. //  Determines size and other information of a message part
  1269. void RE_SetPartInfo(struct Part *rp)
  1270. {
  1271.    int size = rp->Size = FileSize(rp->Filename);
  1272.    if (!rp->Decoded && rp->Nr) switch (rp->EncodingCode)
  1273.    {
  1274.       case ENC_UUE: case ENC_B64: rp->Size = (100*size)/136; break;
  1275.       case ENC_QP:                rp->Size = (100*size)/106; break;
  1276.    }
  1277.    if (!*rp->Name && rp->CParName) { stccpy(rp->Name, rp->CParName, SIZE_FILE); UnquoteString(rp->Name, FALSE); }
  1278.    switch (rp->Nr)
  1279.    {
  1280.       case 0:  SetComment(rp->Filename, GetStr(MSG_RE_Header)); break;
  1281.       case 1:  SetComment(rp->Filename, GetStr(MSG_RE_Letter)); break;
  1282.       default: SetComment(rp->Filename, *rp->Description ? rp->Description : rp->Name); break;
  1283.    }
  1284.    rp->Printable = !strnicmp(rp->ContentType, "text", 4) || rp->Nr == 0;
  1285. }
  1286. ///
  1287. /// RE_ParseMessage
  1288. //  Parses a complete message
  1289. struct Part *RE_ParseMessage(int winnum, FILE *in, char *fname, struct Part *hrp)
  1290. {
  1291.    if (fname) in = fopen(fname, "r");
  1292.    if (in)
  1293.    {
  1294.       FILE *out;
  1295.       struct Part *rp;
  1296.       char *boundary;
  1297.       if (!hrp) if (out = RE_OpenNewPart(winnum, &hrp, NULL, NULL))
  1298.       {
  1299.          BOOL parse_ok = RE_ScanHeader(hrp, in, out, 0);
  1300.          fclose(out);
  1301.          if (parse_ok) RE_SetPartInfo(hrp);
  1302.       }
  1303.       else ER_NewError(GetStr(MSG_ER_CantCreateTempfile), NULL, NULL);
  1304.       if (hrp)
  1305.       {
  1306.          if (!(boundary = hrp->CParBndr)) boundary = hrp->JunkParameter;
  1307.          if (!strnicmp(hrp->ContentType, "multipart", 9))
  1308.          {
  1309.             if (!boundary) ER_NewError(GetStr(MSG_ER_MissingBoundary), NULL, NULL);
  1310.             else
  1311.             {
  1312.                BOOL done;
  1313.                if (*boundary == '"') boundary = UnquoteString(boundary, TRUE);
  1314.                sprintf(hrp->Boundary, "--%s", boundary);
  1315.                done = RE_ConsumeRestOfPart(in, NULL, NULL, hrp);
  1316.                rp = hrp;
  1317.                while (!done)
  1318.                {
  1319.                   struct Part *prev = rp, *newrp;
  1320.                   out = RE_OpenNewPart(winnum, &rp, prev, hrp);
  1321.                   if (!RE_ScanHeader(rp, in, out, 1)) break;
  1322.                   if (!strnicmp(rp->ContentType, "multipart", 9))
  1323.                   {
  1324.                      fclose(out);
  1325.                      if (newrp = RE_ParseMessage(winnum, in, NULL, rp))
  1326.                      {
  1327.                         RE_UndoPart(rp);
  1328.                         done = RE_ConsumeRestOfPart(in, NULL, NULL, prev);
  1329.                         for (rp = prev; rp->Next; rp = rp->Next);
  1330.                      }
  1331.                   }
  1332.                   else if (RE_SaveThisPart(rp) || RE_RequiresSpecialHandling(hrp) == 3)
  1333.                   {
  1334.                      fputc('\n', out);
  1335.                      done = RE_ConsumeRestOfPart(in, out, NULL, rp);
  1336.                      fclose(out);
  1337.                      RE_SetPartInfo(rp);
  1338.                   }
  1339.                   else
  1340.                   {
  1341.                      fclose(out);
  1342.                      done = RE_ConsumeRestOfPart(in, NULL, NULL, rp);
  1343.                      RE_UndoPart(rp);
  1344.                      rp = prev;
  1345.                   }
  1346.                }
  1347.             }
  1348.          }
  1349.          else if (out = RE_OpenNewPart(winnum, &rp, hrp, hrp))
  1350.          {
  1351.             if (RE_SaveThisPart(rp) || RE_RequiresSpecialHandling(hrp) == 3)
  1352.             {
  1353.                RE_ConsumeRestOfPart(in, out, NULL, NULL); fclose(out);
  1354.                RE_SetPartInfo(rp);
  1355.             }
  1356.             else
  1357.             {
  1358.                fclose(out); RE_UndoPart(rp);
  1359.                RE_ConsumeRestOfPart(in, NULL, NULL, NULL);
  1360.             }
  1361.          }
  1362.       }
  1363.       if (fname) fclose(in);
  1364.    } 
  1365.    return hrp;
  1366. }
  1367. ///
  1368. /// RE_DecodePart
  1369. //  Decodes a single message part
  1370. BOOL RE_DecodePart(struct Part *rp)
  1371. {
  1372.    if (!rp->Decoded)
  1373.    {
  1374.       FILE *in, *out;
  1375.       char file[SIZE_FILE], buf[SIZE_LINE], ext[FNSIZE];
  1376.       if (in = fopen(rp->Filename, "r"))
  1377.       {
  1378.          if (rp->HasHeaders) while (GetLine(in, buf, SIZE_LINE)) if (!*buf) break;
  1379.          stcgfe(ext, rp->Name);
  1380.          if (strlen(ext) > 10) *ext = 0;
  1381.          sprintf(file, "YAMmsg-w%ldp%ld.%s", rp->Win, rp->Nr, *ext ? ext : "tmp");
  1382.          strmfp(buf, C->TempDir, file);
  1383.          if (out = fopen(buf, "w"))
  1384.          {
  1385.             RE_DecodeStream(rp, in, out);
  1386.             fclose(out);
  1387.             fclose(in);
  1388.             DeleteFile(rp->Filename);
  1389.             strcpy(rp->Filename, buf);
  1390.             rp->Decoded = TRUE;
  1391.             RE_SetPartInfo(rp);
  1392.          }
  1393.          else fclose(in);
  1394.       }
  1395.    }
  1396.    return rp->Decoded;
  1397. }
  1398. ///
  1399. /// RE_CleanupMessage
  1400. //  Cleanup memory and temporary files used to display the message
  1401. void RE_CleanupMessage(int winnum)
  1402. {
  1403.    struct RE_ClassData *re = G->RE[winnum];
  1404.    struct Part *part, *next;
  1405.  
  1406.    for (part = re->FirstPart; part; part = next)
  1407.    {
  1408.       next = part->Next;
  1409.       if (*part->Filename) DeleteFile(part->Filename);
  1410.       if (part->ContentType) FreeStrBuf(part->ContentType);
  1411.       free(part);
  1412.    }
  1413.    re->FirstPart     = NULL;
  1414.    re->FirstReadDone = FALSE;
  1415.    FinishUnpack(re->File);
  1416.  
  1417. }
  1418. ///
  1419. /// RE_HandleMDNReport
  1420. //  Translates a message disposition notification to readable text
  1421. void RE_HandleMDNReport(struct Part *frp)
  1422. {
  1423.    struct Part *rp[3];
  1424.    char file[SIZE_FILE], buf[SIZE_PATHFILE], MDNtype[SIZE_DEFAULT];
  1425.    char *msgdesc, *mode = "", *type;
  1426.    int i, j;
  1427.    FILE *out, *fh;
  1428.  
  1429.    if (rp[0] = frp->Next) if (rp[1] = rp[0]->Next)
  1430.    {
  1431.       rp[2] = rp[1]->Next;
  1432.       msgdesc = AllocStrBuf(80);
  1433.       strcpy(MDNtype, "");
  1434.       for (j = 1; j < (rp[2] ? 3 : 2); j++)
  1435.       {
  1436.          RE_DecodePart(rp[j]);
  1437.          if (fh = fopen(rp[j]->Filename, "r"))
  1438.          {
  1439.             MA_ReadHeader(fh);
  1440.             fclose(fh);
  1441.             for (i = 0; i < Header.Used; i++)
  1442.             {
  1443.                char *value, *field = Header.Data[i];
  1444.                if (value = strchr(field, ':'))
  1445.                {
  1446.                   *value++ = 0;
  1447.                   if (!stricmp(field, "from")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNFrom)), value);
  1448.                   else if (!stricmp(field, "to")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNTo)), value);
  1449.                   else if (!stricmp(field, "subject")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNSubject)), value);
  1450.                   else if (!stricmp(field, "original-message-id")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNMessageID)), value);
  1451.                   else if (!stricmp(field, "date")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNDate)), value);
  1452.                   else if (!stricmp(field, "original-recipient")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNOrigRecpt)), value);
  1453.                   else if (!stricmp(field, "final-recipient")) msgdesc = StrBufCat(StrBufCat(msgdesc, GetStr(MSG_RE_MDNFinalRecpt)), value);
  1454.                   else if (!stricmp(field, "disposition")) stccpy(MDNtype, Trim(value), SIZE_DEFAULT);
  1455.                }
  1456.             }
  1457.             FreeData2D(&Header);
  1458.          }
  1459.       }
  1460.       msgdesc = StrBufCat(msgdesc, "\n");
  1461.       if (!strnicmp(MDNtype, "manual-action", 13)) mode = GetStr(MSG_RE_MDNmanual);
  1462.       if (!strnicmp(MDNtype, "automatic-action", 16)) mode = GetStr(MSG_RE_MDNauto);
  1463.       if (type = strchr(MDNtype, ';')) type = Trim(++type); else type = MDNtype;
  1464.       sprintf(file, "YAMmsg-w%ldp%ld.txt", rp[0]->Win, rp[0]->Nr);
  1465.       strmfp(buf, C->TempDir, file);
  1466.       if (out = fopen(buf, "w"))
  1467.       {
  1468.          if      (!stricmp(type, "displayed"))  fprintf(out, GetStr(MSG_RE_MDNdisplay), msgdesc);
  1469.          else if (!stricmp(type, "processed"))  fprintf(out, GetStr(MSG_RE_MDNprocessed), msgdesc, mode);
  1470.          else if (!stricmp(type, "dispatched")) fprintf(out, GetStr(MSG_RE_MDNdispatched), msgdesc, mode);
  1471.          else if (!stricmp(type, "deleted"))    fprintf(out, GetStr(MSG_RE_MDNdeleted), msgdesc, mode);
  1472.          else if (!stricmp(type, "denied"))     fprintf(out, GetStr(MSG_RE_MDNdenied), msgdesc);
  1473.          else fprintf(out, GetStr(MSG_RE_MDNunknown), msgdesc, type, mode);
  1474.          fclose(out);
  1475.          DeleteFile(rp[0]->Filename);
  1476.          strcpy(rp[0]->Filename, buf);
  1477.          rp[0]->Decoded = TRUE;
  1478.          RE_SetPartInfo(rp[0]);
  1479.          if (rp[2]) RE_UndoPart(rp[2]);
  1480.          RE_UndoPart(rp[1]);
  1481.       }
  1482.       FreeStrBuf(msgdesc);
  1483.    }
  1484. }
  1485. ///
  1486. /// RE_HandleSignedMessage
  1487. //  Handles a PGP signed message, checks validity of signature
  1488. void RE_HandleSignedMessage(struct Part *frp)
  1489. {
  1490.    struct Part *rp[2];
  1491.  
  1492.    if (rp[0] = frp->Next)
  1493.    {
  1494.       if (*C->PGPCmdPath && (rp[1] = rp[0]->Next))
  1495.       {
  1496.          int error;
  1497.          struct TempFile *tf = OpenTempFile(NULL);
  1498.          char options[SIZE_LARGE];
  1499.          G->RE[frp->Win]->PGPSigned |= PGPS_MIME;
  1500.          ConvertCRLF(rp[0]->Filename, tf->Filename, TRUE);
  1501.          sprintf(options, (G->PGPVersion == 5) ? "%s -o %s +batchmode=1 +force +language=us" : "%s %s +bat +f", rp[1]->Filename, tf->Filename);
  1502.          error = PGPCommand((G->PGPVersion == 5) ? "pgpv": "pgp", options, NOERRORS|KEEPLOG);
  1503.          if (error > 0) G->RE[frp->Win]->PGPSigned |= PGPS_BADSIG;
  1504.          if (error >= 0) RE_GetSigFromLog(frp->Win, NULL);
  1505.          tf->FP = NULL; CloseTempFile(tf);
  1506.       }
  1507.       RE_DecodePart(rp[0]);
  1508.    }
  1509. }
  1510. ///
  1511. /// RE_DecryptPGP
  1512. //  Decrypts a PGP encrypted file
  1513. int RE_DecryptPGP(int winnum, char *src)
  1514. {
  1515.    FILE *fh;
  1516.    int error;
  1517.    char options[SIZE_LARGE], orcpt[SIZE_ADDRESS];
  1518.  
  1519.    *orcpt = 0;
  1520.    PGPGetPassPhrase();
  1521.    if (G->PGPVersion == 5)
  1522.    {
  1523.       char fname[SIZE_PATHFILE];
  1524.       sprintf(fname, "%s.asc", src); Rename(src, fname);
  1525.       sprintf(options, "%s +batchmode=1 +force +language=us", fname);
  1526.       error = PGPCommand("pgpv", options, KEEPLOG|NOERRORS);
  1527.       RE_GetSigFromLog(winnum, orcpt);
  1528.       if (*orcpt) error = 2;
  1529.       DeleteFile(fname);
  1530.    }
  1531.    else
  1532.    {
  1533.       sprintf(options, "%s +bat +f", src);
  1534.       error = PGPCommand("pgp", options, KEEPLOG|NOERRORS);
  1535.       RE_GetSigFromLog(winnum, NULL);
  1536.    }
  1537.    PGPClearPassPhrase(error < 0 || error > 1);
  1538.    if (error < 0 || error > 1) if (fh = fopen(src, "w"))
  1539.    {
  1540.       fprintf(fh, GetStr(MSG_RE_PGPNotAllowed));
  1541.       if (G->PGPVersion == 5 && *orcpt) fprintf(fh, GetStr(MSG_RE_MsgReadOnly), orcpt);
  1542.       fclose(fh);
  1543.    }
  1544.    return error;
  1545. }
  1546. ///
  1547. /// RE_HandleEncryptedMessage
  1548. //  Handles a PGP encryped message
  1549. void RE_HandleEncryptedMessage(struct Part *frp)
  1550. {
  1551.    struct Part *rp[2];
  1552.    FILE *in;
  1553.    if (rp[0] = frp->Next) if (rp[1] = rp[0]->Next)
  1554.    {
  1555.       if (!RE_DecryptPGP(frp->Win, rp[1]->Filename))
  1556.       {
  1557.          G->RE[frp->Win]->PGPSigned |= PGPS_OLD;
  1558.       }
  1559.       G->RE[frp->Win]->PGPEncrypted |= PGPE_MIME;
  1560.       if (ConvertCRLF(rp[1]->Filename, rp[0]->Filename, FALSE)) if (in = fopen(rp[0]->Filename, "r"))
  1561.       {
  1562.          rp[0]->ContentType = StrBufCpy(rp[0]->ContentType, "text/plain");
  1563.          rp[0]->Printable = TRUE; rp[0]->EncodingCode = ENC_NONE;
  1564.          *rp[0]->Description = 0;
  1565.          RE_ScanHeader(rp[0], in, NULL, 2);
  1566.          fclose(in);
  1567.          rp[0]->Decoded = FALSE; RE_DecodePart(rp[0]);
  1568.          RE_UndoPart(rp[1]);
  1569.       }
  1570.    }
  1571. }
  1572. ///
  1573. /// RE_LoadMessagePart
  1574. //  Decodes a single message part
  1575. void RE_LoadMessagePart(int winnum, struct Part *part)
  1576. {
  1577.    struct Part *rp, *next;
  1578.    int rsh = RE_RequiresSpecialHandling(part);
  1579.  
  1580.    switch (rsh)
  1581.    {
  1582.       case 1:  RE_HandleMDNReport(part); break;
  1583.       case 2:  RE_HandleSignedMessage(part); break;
  1584.       case 3:  RE_HandleEncryptedMessage(part); break;
  1585.       default:
  1586.       for (rp = part->Next; rp; rp = next)
  1587.       {
  1588.          next = rp->Next;
  1589.          if (RE_IsURLencoded(rp))
  1590.          {
  1591.             rp->ContentType = StrBufCpy(rp->ContentType, "text/plain");
  1592.             rp->EncodingCode = ENC_FORM;
  1593.             RE_DecodePart(rp);
  1594.          }
  1595.          else if (!stricmp(rp->ContentType, "application/pgp-keys"))
  1596.             G->RE[winnum]->PGPKey = TRUE;
  1597.          else if (rp->Nr < 2 || (rp->Printable && C->DisplayAllTexts)) RE_DecodePart(rp);
  1598.       }
  1599.    }
  1600. }
  1601. ///
  1602. /// RE_LoadMessage
  1603. //  Prepares a message for displaying
  1604. BOOL RE_LoadMessage(int winnum, int parsemode)
  1605. {
  1606.    char newfile[SIZE_PATHFILE], file[SIZE_FILE];
  1607.    struct Part *rp;
  1608.    int i;
  1609.    Busy(GetStr(MSG_BusyReading), "", 0, 0);
  1610.    RE_CleanupMessage(winnum);
  1611.    if (!StartUnpack(G->RE[winnum]->File, newfile, G->RE[winnum]->MailPtr->Folder)) return FALSE;
  1612.    strcpy(G->RE[winnum]->File, newfile);
  1613.    G->RE[winnum]->ParseMode = parsemode;
  1614.    if (rp = G->RE[winnum]->FirstPart = RE_ParseMessage(winnum, NULL, G->RE[winnum]->File, NULL))
  1615.    {
  1616.       RE_LoadMessagePart(winnum, rp);
  1617.       for (i = 0; rp; i++, rp = rp->Next) if (rp->Nr != i)
  1618.       {
  1619.          rp->Nr = i;
  1620.          sprintf(file, "YAMmsg-w%ldp%ld%s", winnum, i, strchr(rp->Filename,'.'));
  1621.          strmfp(newfile, C->TempDir, file);
  1622.          RenameFile(rp->Filename, newfile);
  1623.          strcpy(rp->Filename, newfile);
  1624.       }
  1625.    }
  1626.    BusyEnd;
  1627.    return TRUE;
  1628. }
  1629. ///
  1630. /// RE_GetPart
  1631. //  Gets a message part by its index number
  1632. struct Part *RE_GetPart(int winnum, int partnr)
  1633. {
  1634.    struct Part *part;
  1635.    for (part = G->RE[winnum]->FirstPart; part; part = part->Next) if (part->Nr == partnr) break;
  1636.    return part;
  1637. }
  1638. ///
  1639. /// RE_InitPrivateRC
  1640. //  Allocates resources for background message parsing
  1641. void RE_InitPrivateRC(struct Mail *mail, int parsemode)
  1642. {
  1643.    G->RE[4] = calloc(1,sizeof(struct RE_ClassData));
  1644.    G->RE[4]->Mail = *mail;
  1645.    G->RE[4]->MailPtr = mail;
  1646.    GetMailFile(G->RE[4]->File, mail->Folder, mail);
  1647.    RE_LoadMessage(4, parsemode);
  1648. }
  1649. ///
  1650. /// RE_FreePrivateRC
  1651. //  Frees resources used by background message parsing
  1652. void RE_FreePrivateRC(void)
  1653. {
  1654.    RE_CleanupMessage(4);
  1655.    free(G->RE[4]);
  1656. }
  1657. ///
  1658. /// AppendToBuffer
  1659. //  Appends a string to a dynamic-length buffer
  1660. char *AppendToBuffer(char *buf, int *wptr, int *len, char *add)
  1661. {
  1662.    int nlen = *len, npos = (*wptr)+strlen(add);
  1663.    while (npos >= nlen-1) nlen = (nlen*3)/2;
  1664.    if (nlen != *len) buf = realloc(buf, *len = nlen);
  1665.    while (*add) buf[(*wptr)++] = *add++;
  1666.    return buf;
  1667. }
  1668. ///
  1669. /// RE_ExtractURL
  1670. //  Extracts URL from a message line
  1671. BOOL RE_ExtractURL(char *line, char *url, char **urlptr, char **rest)
  1672. {
  1673.    char *protocols[7] = { "mailto:", "http://", "https://", "ftp://", "gopher://", "telnet://", "news:" };
  1674.    char *legalchars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@_?+-,.~/%&=:*#";
  1675.    char *foundurl = NULL, *p;
  1676.    int i;
  1677.    if (p = strchr(line, ':')) for (i = 0; i < 7; i++) if (foundurl = stristr(line, protocols[i])) break;
  1678.    if (!foundurl) return FALSE;
  1679.    for (i = 0; foundurl[i] && strchr(legalchars, foundurl[i]) && i < SIZE_URL-1; i++) url[i] = foundurl[i];
  1680.    if (strchr(".?!", url[i-1])) --i;
  1681.    url[i] = 0;
  1682.    if (urlptr) *urlptr = foundurl;
  1683.    if (rest) *rest = &foundurl[i];
  1684.    return TRUE;
  1685. }
  1686. ///
  1687. /// RE_ReadInMessage
  1688. //  Reads a message into the display buffer
  1689. char *RE_ReadInMessage(int winnum, int mode)
  1690. {
  1691.    struct RE_ClassData *re = G->RE[winnum];
  1692.    struct Part *part, *uup = NULL, *last, *first = re->FirstPart;
  1693.    char buffer[SIZE_LARGE], *msg, *cmsg, *ptr, *rptr, *eolptr, url[SIZE_URL], *urlptr, *tsb, *sb, *bo, *pl;
  1694.    int totsize, len, wptr;
  1695.    FILE *fh;
  1696.    
  1697.    if (re->NoTextstyles) { tsb = "\033c\033[s:18]\033l"; sb = "\033[s:2]"; bo = "\033b"; pl = "\033n"; }
  1698.    else { tsb = "<tsb>"; sb = "<sb>"; bo = "*"; pl = "*"; }
  1699.    for (totsize = 1000, part = first; part; part = part->Next)
  1700.    {
  1701.       if (mode != RIM_READ && part->Nr && part->Nr != C->LetterPart) continue;
  1702.       if (part->Decoded || !part->Nr) totsize += part->Size; else totsize += 200;
  1703.    }
  1704.    if (cmsg = calloc(len=(totsize*3)/2,1))
  1705.    {
  1706.       if (mode != RIM_QUIET) Busy(GetStr(MSG_BusyDisplaying), "", 0, 0);
  1707.       wptr = 0;
  1708.       if (mode == RIM_READ)
  1709.          if (fh = fopen(first->Filename, "r"))
  1710.          {
  1711.             int buflen = re->FirstPart->MaxHeaderLen+4;
  1712.             char *linebuf = malloc(buflen);
  1713.             while (fgets(linebuf, buflen, fh))
  1714.                cmsg = AppendToBuffer(cmsg, &wptr, &len, linebuf);
  1715.             free(linebuf);
  1716.             fclose(fh);
  1717.             cmsg = AppendToBuffer(cmsg, &wptr, &len, "\n");
  1718.          }
  1719.       for (part = first->Next; part; part = part->Next)
  1720.       {
  1721.          BOOL dodisp = (part->Printable && part->Decoded);
  1722.          if (mode != RIM_READ && part->Nr > 1) break;
  1723.          if (mode == RIM_READ && (part->Nr > 1 || !dodisp))
  1724.          {
  1725.             *buffer = 0; sprintf(buffer, "%s%ld: %s\n%s%s:%s %s   %s%s:%s %ld %s\n", tsb, part->Nr, part->Name, bo, GetStr(MSG_RE_ContentType), pl, DescribeCT(part->ContentType), bo, GetStr(MSG_Size), pl, part->Size, GetStr(MSG_Bytes));
  1726.             if (*buffer) cmsg = AppendToBuffer(cmsg, &wptr, &len, buffer);
  1727.             *buffer = 0; if (*part->Description) sprintf(&buffer[strlen(buffer)], "%s%s:%s %s\n", bo, GetStr(MSG_RE_Description), pl, part->Description);
  1728.             if (dodisp) { strcat(buffer, sb); strcat(buffer, "\n"); }
  1729.             if (*buffer) cmsg = AppendToBuffer(cmsg, &wptr, &len, buffer);
  1730.          }
  1731.          if (dodisp)
  1732.          {
  1733.             if (fh = fopen(part->Filename, "r"))
  1734.             {       
  1735.                if (msg = calloc(part->Size+3,1))
  1736.                {
  1737.                   *msg = '\n';
  1738.                   fread(msg+1, 1, part->Size, fh);
  1739.                   rptr = msg+1;
  1740.                   while (*rptr)
  1741.                   {
  1742.                      for (eolptr = rptr; *eolptr && *eolptr != '\n'; eolptr++); *eolptr = 0;
  1743. /* UUencoded */      if (!strncmp(rptr, "begin ", 6) && isdigit((int)rptr[6]))
  1744.                      {
  1745.                         if (!re->FirstReadDone)
  1746.                         {
  1747.                            FILE *ufh;
  1748.                            ptr = &rptr[6];
  1749.                            while (!ISpace(*ptr)) ptr++;
  1750.                            ptr = stpblk(ptr);
  1751.                            for (last = first; last->Next; last = last->Next);
  1752.                            if (ufh = RE_OpenNewPart(winnum, &uup, last, first))
  1753.                            {
  1754.                               uup->ContentType = StrBufCpy(uup->ContentType, "application/octet-stream");
  1755.                               strcpy(uup->Description, GetStr(MSG_RE_UUencodedFile));
  1756.                               stccpy(uup->Name, ptr, SIZE_FILE);
  1757.                               fromuuetxt(&rptr, ufh);
  1758.                               fclose(ufh);
  1759.                               uup->Decoded = TRUE;
  1760.                               RE_SetPartInfo(uup);
  1761.                               eolptr = rptr-1; ptr = rptr;
  1762.                            }
  1763.                            else ER_NewError(GetStr(MSG_ER_CantCreateTempfile), NULL, NULL);
  1764.                         }
  1765.                         else
  1766.                         {
  1767.                            for (ptr=eolptr+1; *ptr; ptr++)
  1768.                            {
  1769.                               if (!strncmp(ptr, "end", 3)) break;
  1770.                               while (*ptr && *ptr != '\n') ptr++;
  1771.                            }
  1772.                            while (*ptr && *ptr != '\n') ptr++; eolptr = ptr++;
  1773.                         }
  1774.                         if (!strncmp(ptr, "size", 4))
  1775.                         {
  1776.                            if (!re->FirstReadDone)
  1777.                            {
  1778.                               int expsize = atoi(&ptr[5]);
  1779.                               if (uup->Size != expsize) ER_NewError(GetStr(MSG_ER_UUSize), (char *)uup->Size, (char *)expsize);
  1780.                            }
  1781.                            for (eolptr = ptr; *eolptr && *eolptr!='\n'; eolptr++); *eolptr = 0;
  1782.                         }
  1783.                         goto rim_cont;
  1784.                      }
  1785. /* PGP message */    if (!strncmp(rptr, "-----BEGIN PGP MESSAGE", 21))
  1786.                      {
  1787.                         struct TempFile *tf;
  1788.                         if (tf = OpenTempFile("w"))
  1789.                         {
  1790.                            *eolptr = '\n';
  1791.                            for (ptr=eolptr+1; *ptr; ptr++)
  1792.                            {
  1793.                               if (!strncmp(ptr, "-----END PGP MESSAGE", 19)) break;
  1794.                               while (*ptr && *ptr != '\n') ptr++;
  1795.                            }
  1796.                            while (*ptr && *ptr != '\n') ptr++; eolptr = ptr++;
  1797.                            fwrite(rptr, 1, ptr-rptr, tf->FP);
  1798.                            fclose(tf->FP); tf->FP = NULL;
  1799.                            if (!RE_DecryptPGP(winnum, tf->Filename)) re->PGPSigned |= PGPS_OLD;
  1800.                            if (tf->FP = fopen(tf->Filename, "r"))
  1801.                            {
  1802.                               char buf2[SIZE_LARGE];
  1803.                               while (fgets(buf2, SIZE_LARGE, tf->FP))
  1804.                               {
  1805.                                  rptr = buf2;
  1806.                                  cmsg = AppendToBuffer(cmsg, &wptr, &len, buf2);
  1807.                               }
  1808.                            }
  1809.                            CloseTempFile(tf);
  1810.                         }
  1811.                         re->PGPEncrypted |= PGPE_OLD;
  1812.                         goto rim_cont;
  1813.                      }
  1814. /* signature */      if (!strcmp(rptr, "-- "))
  1815.                      {
  1816.                         if (mode == RIM_QUOTE && C->StripSignature) break;
  1817.                         else if (mode == RIM_READ)
  1818.                         {
  1819.                            if (C->SigSepLine == 1) cmsg = AppendToBuffer(cmsg, &wptr, &len, rptr);
  1820.                            if (C->SigSepLine == 2) cmsg = AppendToBuffer(cmsg, &wptr, &len, sb);
  1821.                            if (C->SigSepLine == 3) break;
  1822.                            cmsg = AppendToBuffer(cmsg, &wptr, &len, "\n");
  1823.                            goto rim_cont;
  1824.                         }
  1825.                      }
  1826. /* URL */            if (!re->NoTextstyles && mode == RIM_READ) if (RE_ExtractURL(rptr, url, &urlptr, &ptr))
  1827.                      {
  1828.                         char *buf2, *p;
  1829.                         if (buf2 = calloc(SIZE_DEFAULT+(strlen(rptr)*3)/2,1))
  1830.                         {
  1831.                            p = buf2;
  1832.                            do
  1833.                            {
  1834.                               while (rptr < urlptr) *p++ = *rptr++;
  1835.                               sprintf(p, "\033p[7]%s\033p[0]", url);
  1836.                               p = &buf2[strlen(buf2)]; rptr = ptr;
  1837.                            } while (RE_ExtractURL(rptr, url, &urlptr, &ptr));
  1838.                            strcpy(p, rptr); strcat(p, "\n");
  1839.                            cmsg = AppendToBuffer(cmsg, &wptr, &len, buf2);
  1840.                            free(buf2);
  1841.                            goto rim_cont;
  1842.                         }
  1843.                      }
  1844.                      if (!strncmp(rptr, "-----BEGIN PGP PUBLIC KEY BLOCK", 31)) re->PGPKey = TRUE;
  1845.                      if (!strncmp(rptr, "-----BEGIN PGP SIGNED MESSAGE", 29)) re->PGPSigned |= PGPS_OLD;
  1846.                      cmsg = AppendToBuffer(cmsg, &wptr, &len, rptr);
  1847.                      cmsg = AppendToBuffer(cmsg, &wptr, &len, "\n");
  1848. rim_cont:
  1849.                      rptr = eolptr+1;
  1850.                      if (mode == RIM_QUIET) DoMethod(G->App,MUIM_Application_InputBuffered);
  1851.                   }
  1852.                   free(msg);
  1853.                }
  1854.                fclose(fh);
  1855.             }
  1856.          }
  1857.       }
  1858.       re->FirstReadDone = TRUE;
  1859.       if (mode != RIM_QUIET) BusyEnd;
  1860.    }
  1861.    return cmsg;
  1862. }
  1863. ///
  1864. /// RE_AddExtraHeader
  1865. //  Adds additional headers to the header listview
  1866. void RE_AddExtraHeader(APTR lv, char *header, char *value)
  1867. {
  1868.    char buffer[SIZE_LARGE];
  1869.    if (!*value) return;
  1870.    sprintf(buffer, MUIX_I"%s: %s", StripUnderscore(header), value);
  1871.    DoMethod(lv, MUIM_NList_InsertSingle, buffer, MUIV_NList_Insert_Bottom);
  1872. }
  1873. ///
  1874. /// RE_GetSenderInfo
  1875. //  Parses X-SenderInfo header field
  1876. void RE_GetSenderInfo(struct Mail *mail, struct ABEntry *ab)
  1877. {
  1878.    char *s, *t, *eq;
  1879.    struct ExtendedMail *email;
  1880.  
  1881.    clear(ab, sizeof(struct ABEntry));
  1882.    stccpy(ab->Address, mail->From.Address, SIZE_ADDRESS);
  1883.    stccpy(ab->RealName, mail->From.RealName, SIZE_REALNAME);
  1884.    if (mail->Flags & MFLAG_SENDERINFO)
  1885.    {
  1886.       email = MA_ExamineMail(mail->Folder, mail->MailFile, NULL, TRUE);
  1887.       if (s = strchr(email->SenderInfo, ';'))
  1888.       {
  1889.          *s++ = 0;
  1890.          do {
  1891.             if (t = ParamEnd(s)) *t++ = 0;
  1892.             if (!(eq = strchr(s, '='))) Cleanse(s);
  1893.             else
  1894.             {
  1895.                *eq++ = 0;
  1896.                s = Cleanse(s); eq = stpblk(eq);
  1897.                StripTrailingSpace(eq);
  1898.                UnquoteString(eq, FALSE);
  1899.                if (!stricmp(s, "street")) stccpy(ab->Street, eq, SIZE_DEFAULT);
  1900.                if (!stricmp(s, "city")) stccpy(ab->City, eq, SIZE_DEFAULT);
  1901.                if (!stricmp(s, "country")) stccpy(ab->Country, eq, SIZE_DEFAULT);
  1902.                if (!stricmp(s, "phone")) stccpy(ab->Phone, eq, SIZE_DEFAULT);
  1903.                if (!stricmp(s, "homepage")) stccpy(ab->Homepage, eq, SIZE_URL);
  1904.                if (!stricmp(s, "dob")) ab->BirthDay = atol(eq);
  1905.                if (!stricmp(s, "picture")) stccpy(ab->Photo, eq, SIZE_PATHFILE);
  1906.                ab->Type = 1;
  1907.             }
  1908.             s = t;
  1909.          } while (t);
  1910.       }
  1911.       MA_FreeEMailStruct(email);
  1912.    }
  1913. }
  1914. ///
  1915. /// RE_UpdateSenderInfo
  1916. //  Updates address book entry of sender
  1917. void RE_UpdateSenderInfo(struct ABEntry *old, struct ABEntry *new)
  1918. {
  1919.    BOOL changed = FALSE;
  1920.  
  1921.    if (!*old->RealName && *new->RealName) { strcpy(old->RealName, new->RealName); changed = TRUE; }
  1922.    if (!*old->Address  && *new->Address ) { strcpy(old->Address,  new->Address ); changed = TRUE; }
  1923.    if (!*old->Street   && *new->Street  ) { strcpy(old->Street,   new->Street  ); changed = TRUE; }
  1924.    if (!*old->Country  && *new->Country ) { strcpy(old->Country,  new->Country ); changed = TRUE; }
  1925.    if (!*old->City     && *new->City    ) { strcpy(old->City,     new->City    ); changed = TRUE; }
  1926.    if (!*old->Phone    && *new->Phone   ) { strcpy(old->Phone,    new->Phone   ); changed = TRUE; }
  1927.    if (!*old->Homepage && *new->Homepage) { strcpy(old->Homepage, new->Homepage); changed = TRUE; }
  1928.    if (!old->BirthDay  && new->BirthDay ) { old->BirthDay = new->BirthDay; changed = TRUE; }
  1929.    if (changed) AB_SaveABookFunc();
  1930. }
  1931. ///
  1932. /// RE_AddSenderInfo
  1933. //  Displays sender information to header listview
  1934. void RE_AddSenderInfo(int winnum, struct ABEntry *ab)
  1935. {
  1936.    APTR lv = G->RE[winnum]->GUI.LV_HEAD;
  1937.    RE_AddExtraHeader(lv, GetStr(MSG_EA_RealName), ab->RealName);
  1938.    RE_AddExtraHeader(lv, GetStr(MSG_EA_Street), ab->Street);
  1939.    RE_AddExtraHeader(lv, GetStr(MSG_EA_City), ab->City);
  1940.    RE_AddExtraHeader(lv, GetStr(MSG_EA_Country), ab->Country);
  1941.    RE_AddExtraHeader(lv, GetStr(MSG_EA_Phone), ab->Phone);
  1942.    RE_AddExtraHeader(lv, GetStr(MSG_EA_DOB), AB_ExpandBD(ab->BirthDay));
  1943.    RE_AddExtraHeader(lv, GetStr(MSG_EA_Description), ab->Comment);
  1944.    RE_AddExtraHeader(lv, GetStr(MSG_EA_Homepage), ab->Homepage);
  1945. }
  1946. ///
  1947. /// RE_AddToAddrbook
  1948. //  Adds sender to the address book
  1949. struct ABEntry *RE_AddToAddrbook(APTR win, struct ABEntry *templ)
  1950. {
  1951.    struct ABEntry new;
  1952.    char buf[SIZE_LARGE];
  1953.    BOOL doit = FALSE;
  1954.    switch (C->AddToAddrbook)
  1955.    {        
  1956.       case 1: if (!templ->Type) break;
  1957.       case 2: sprintf(buf, GetStr(MSG_RE_AddSender), BuildAddrName(templ->Address, templ->RealName));
  1958.               doit = MUI_Request(G->App, win, 0, NULL, GetStr(MSG_YesNoReq), buf);
  1959.               break;
  1960.       case 3: if (!templ->Type) break;
  1961.       case 4: doit = TRUE;
  1962.    }
  1963.    if (doit)
  1964.    {
  1965.       struct MUIS_Listtree_TreeNode *tn = MUIV_Lt_Insert_ListNode_Root;
  1966.       int hits = 0;
  1967.  
  1968.       if (*C->NewAddrGroup) if (!AB_SearchEntry(MUIV_Lt_GetEntry_ListNode_Root, C->NewAddrGroup, ASM_ALIAS|ASM_GROUP, &hits, &tn))
  1969.       {
  1970.          clear(&new, sizeof(struct ABEntry));
  1971.          stccpy(new.Alias, C->NewAddrGroup, SIZE_NAME);
  1972.          stccpy(new.Comment, GetStr(MSG_RE_NewGroupTitle), SIZE_DEFAULT);
  1973.          new.Type = AET_GROUP;
  1974.          tn = (struct MUIS_Listtree_TreeNode *)DoMethod(G->AB->GUI.LV_ADRESSES, MUIM_Listtree_Insert, new.Alias, &new, MUIV_Lt_Insert_ListNode_Root, MUIV_Lt_Insert_PrevNode_Sorted, TNF_LIST);
  1975.       }
  1976.       clear(&new, sizeof(struct ABEntry));
  1977.       new.Type = AET_USER;
  1978.       RE_UpdateSenderInfo(&new, templ);
  1979.       EA_SetDefaultAlias(&new);
  1980.       tn = (struct MUIS_Listtree_TreeNode *)DoMethod(G->AB->GUI.LV_ADRESSES, MUIM_Listtree_Insert, new.Alias, &new, tn, MUIV_Lt_Insert_PrevNode_Sorted, 0);
  1981.       if (tn)
  1982.       {
  1983.          AB_SaveABookFunc();
  1984.          return tn->tn_User;
  1985.       }
  1986.    }
  1987.    return NULL;
  1988. }
  1989. ///
  1990. /// RE_FindPhotoOnDisk
  1991. //  Searches portrait of sender in the gallery directory
  1992. BOOL RE_FindPhotoOnDisk(struct ABEntry *ab, char *photo)
  1993. {
  1994.    *photo = 0;
  1995.    if (*ab->Photo) strcpy(photo, ab->Photo);
  1996.    else if (*C->GalleryDir)
  1997.    {
  1998.       char fname[SIZE_FILE];
  1999.       stccpy(fname, ab->RealName, SIZE_FILE);
  2000.       if (PFExists(C->GalleryDir, fname)) strmfp(photo, C->GalleryDir, fname);
  2001.       else
  2002.       {
  2003.          stccpy(fname, ab->Address, SIZE_FILE);
  2004.          if (PFExists(C->GalleryDir, fname)) strmfp(photo, C->GalleryDir, fname);
  2005.       }
  2006.    }
  2007.    if (!*photo) return FALSE;
  2008.    return (BOOL)(FileSize(photo) > 0);
  2009. }
  2010. ///
  2011. /// RE_DownloadPhoto
  2012. //  Downloads portrait photograph of sender from the YAM homepage
  2013. BOOL RE_DownloadPhoto(APTR win, char *url, struct ABEntry *ab)
  2014. {
  2015.    char fname[SIZE_FILE], picfname[SIZE_PATHFILE], ext[SIZE_SMALL];
  2016.    char *name = *ab->Alias ? ab->Alias : "pic";
  2017.    int i;
  2018.    BOOL success = FALSE, doit = FALSE;
  2019.  
  2020.    switch (C->AddToAddrbook)
  2021.    {    
  2022.       case 1: case 2: doit = MUI_Request(G->App, win, 0, NULL, GetStr(MSG_OkayCancelReq), GetStr(MSG_RE_DownloadPhotoReq)); break;
  2023.       case 3: case 4: doit = TR_IsOnline();
  2024.    }
  2025.    if (doit)
  2026.    {
  2027.       if (!stcgfe(ext, url)) strcpy(ext, "iff");
  2028.       sprintf(fname, "%s.%s", name, ext);
  2029.       for (i = 2; PFExists(C->GalleryDir, fname); i++) sprintf(fname, "%s%ld.%s", name, i, ext);
  2030.       strmfp(picfname, C->GalleryDir, fname);
  2031.       if (TR_OpenTCPIP())
  2032.       {
  2033.          Busy(GetStr(MSG_BusyDownloadingPic), name, 0, 0);
  2034.          CreateDirectory(C->GalleryDir);
  2035.          if (TR_DownloadURL(url, NULL, NULL, picfname))
  2036.          {
  2037.             strcpy(ab->Photo, picfname);
  2038.             AB_SaveABookFunc();
  2039.             success = TRUE;
  2040.          }
  2041.          BusyEnd;
  2042.          TR_CloseTCPIP();
  2043.       }
  2044.       else ER_NewError(GetStr(MSG_ER_NoTCP), NULL, NULL);
  2045.    }
  2046.    return success;
  2047. }
  2048. ///
  2049. /// RE_DisplayMessage
  2050. //  Shows message header and body in read window
  2051. void RE_DisplayMessage(int winnum)
  2052. {
  2053.    char *cmsg, *body;
  2054.    BOOL dispheader;
  2055.    struct RE_GUIData *gui = &(G->RE[winnum]->GUI);
  2056.    struct Person *from = &G->RE[winnum]->Mail.From;
  2057.    struct MUIS_Listtree_TreeNode *tn;
  2058.    struct ABEntry *ab = NULL, abtmpl;
  2059.    int hits = 0;
  2060.  
  2061.    if (cmsg = RE_ReadInMessage(winnum, RIM_READ))
  2062.    {
  2063.       dispheader = G->RE[winnum]->Header != 0;
  2064.       set(gui->GR_HEAD, MUIA_ShowMe, dispheader);
  2065.       set(gui->BO_BALANCE, MUIA_ShowMe, dispheader);
  2066.       DoMethod(gui->LV_HEAD, MUIM_NList_Clear);
  2067.       set(gui->LV_HEAD, MUIA_NList_Quiet, TRUE);
  2068.       body = cmsg;
  2069.       while (*body)
  2070.       {
  2071.          if (*body == '\n') { body++; break; }
  2072.          dispheader = G->RE[winnum]->Header == 2;
  2073.          if (G->RE[winnum]->Header == 1)
  2074.          {
  2075.             char header[SIZE_DEFAULT];
  2076.             int i;
  2077.             for (i = 0; !strchr("\n :", body[i]) && i < SIZE_DEFAULT-1; i++) header[i] = body[i];
  2078.             header[i] = 0;
  2079.             dispheader = MatchNoCase(header, C->ShortHeaders);
  2080.          }
  2081.          if (dispheader) DoMethod(gui->LV_HEAD, MUIM_NList_InsertSingleWrap, body, MUIV_NList_Insert_Bottom, G->RE[winnum]->WrapHeader ? WRAPCOL1 : NOWRAP, ALIGN_LEFT);
  2082.          while (*body && *body != '\n') body++;
  2083.          if (*body) body++;
  2084.       }
  2085.       if (!AB_SearchEntry(MUIV_Lt_GetEntry_ListNode_Root, from->Address, ASM_ADDRESS|ASM_USER, &hits, &tn) && *from->RealName)
  2086.            AB_SearchEntry(MUIV_Lt_GetEntry_ListNode_Root, from->RealName, ASM_REALNAME|ASM_USER, &hits, &tn);
  2087.       if (hits) ab = tn->tn_User;
  2088.       RE_GetSenderInfo(G->RE[winnum]->MailPtr, &abtmpl);
  2089.       if (!stricmp(from->Address, C->EmailAddress) || !stricmp(from->RealName, C->RealName))
  2090.       {
  2091.          if (!ab) { ab = &abtmpl; *ab->Photo = 0; }
  2092.       }
  2093.       else
  2094.       {
  2095.          if (ab)
  2096.          {
  2097.             RE_UpdateSenderInfo(ab, &abtmpl);
  2098.             if (!*ab->Photo && *abtmpl.Photo && *C->GalleryDir) RE_DownloadPhoto(gui->WI, abtmpl.Photo, ab);
  2099.          }
  2100.          else
  2101.          {
  2102.             if (ab = RE_AddToAddrbook(gui->WI, &abtmpl))
  2103.             {
  2104.                if (*abtmpl.Photo && *C->GalleryDir) RE_DownloadPhoto(gui->WI, abtmpl.Photo, ab);
  2105.             }
  2106.             else { ab = &abtmpl; *ab->Photo = 0; }
  2107.          }
  2108.       }
  2109.       if (G->RE[winnum]->SenderInfo)
  2110.       {
  2111.          if (hits || ab->Type == 1) RE_AddSenderInfo(winnum, ab);
  2112.          if (G->RE[winnum]->SenderInfo == 2) if (DoMethod(gui->GR_PHOTO, MUIM_Group_InitChange))
  2113.          {
  2114.             char photopath[SIZE_PATHFILE];
  2115.             if (gui->BC_PHOTO)
  2116.             {
  2117.                DoMethod(gui->GR_PHOTO, OM_REMMEMBER, gui->BC_PHOTO);
  2118.                MUI_DisposeObject(gui->BC_PHOTO);
  2119.             }
  2120.             gui->BC_PHOTO = NULL;
  2121.             if (RE_FindPhotoOnDisk(ab, photopath))
  2122.             {
  2123.                gui->BC_PHOTO = MakePicture(photopath);
  2124.                DoMethod(gui->GR_PHOTO, OM_ADDMEMBER, gui->BC_PHOTO);
  2125.             }
  2126.             DoMethod(gui->GR_PHOTO, MUIM_Group_ExitChange);
  2127.          }
  2128.       }
  2129.       set(gui->GR_INFO, MUIA_ShowMe, (G->RE[winnum]->SenderInfo == 2) && (gui->BC_PHOTO != NULL));
  2130.       set(gui->LV_HEAD, MUIA_NList_Quiet, FALSE);
  2131.       set(gui->TE_TEXT, MUIA_TextEditor_ImportHook, G->RE[winnum]->NoTextstyles ? MUIV_TextEditor_ImportHook_Plain : MUIV_TextEditor_ImportHook_EMail);
  2132.       set(gui->TE_TEXT, MUIA_TextEditor_Contents, body);
  2133.       free(cmsg);
  2134.    }
  2135. }
  2136. ///
  2137. /// RE_ClickedOnMessage
  2138. //  User clicked on a e-mail address
  2139. void RE_ClickedOnMessage(char *address)
  2140. {
  2141.    struct MUIS_Listtree_TreeNode *tn;
  2142.    struct ABEntry *ab = NULL;
  2143.    int l, win, hits = 0;
  2144.    char *p, *gads, buf[SIZE_LARGE], *body = NULL, *subject = NULL;
  2145.    if (l = strlen(address)) if (strchr(".?!", address[--l])) address[l] = 0;
  2146.    for (p = strchr(address, '&'); p; p = strchr(p, '&'))
  2147.    {
  2148.       *p++ = 0;
  2149.       if (!strnicmp(p, "body=", 5)) body = &p[5];
  2150.       if (!strnicmp(p, "subject=", 8)) subject = &p[8];
  2151.    }
  2152.    if (AB_SearchEntry(MUIV_Lt_GetEntry_ListNode_Root, address, ASM_ADDRESS|ASM_USER|ASM_LIST, &hits, &tn)) ab = tn->tn_User;
  2153.    sprintf(buf, GetStr(MSG_RE_SelectAddressReq), address);
  2154.    gads = GetStr(hits ? MSG_RE_SelectAddressEdit : MSG_RE_SelectAddressAdd);
  2155.    switch (MUI_Request(G->App, G->MA->GUI.WI, 0, NULL, gads, buf))
  2156.    {
  2157.       case 1: if ((win = MA_NewNew(NULL, 0)) >= 0)
  2158.               {
  2159.                  struct WR_GUIData *gui = &G->WR[win]->GUI;
  2160.                  setstring(gui->ST_TO, hits ? BuildAddrName(address, ab->RealName) : address);
  2161.                  if (subject) setstring(gui->ST_SUBJECT, subject);
  2162.                  if (body) set(gui->TE_EDIT, MUIA_TextEditor_Contents, body);
  2163.                  set(gui->WI, MUIA_Window_ActiveObject, gui->ST_SUBJECT);
  2164.               }
  2165.               break;
  2166.       case 2: DoMethod(G->App, MUIM_CallHook, &AB_OpenHook, ABM_EDIT);
  2167.               if (hits)
  2168.               {
  2169.                  if ((win = EA_Init(ab->Type, tn)) >= 0) EA_Setup(win, ab);
  2170.               }
  2171.               else
  2172.               {
  2173.                  if ((win = EA_Init(AET_USER, NULL)) >= 0) setstring(G->EA[win]->GUI.ST_ADDRESS, address);
  2174.               }
  2175.               break;
  2176.    }
  2177. }
  2178. ///
  2179. /// RE_DoubleClickFunc
  2180. //  Handles double-clicks on an URL
  2181. SAVEDS ASM BOOL RE_DoubleClickFunc(REG(a1) struct ClickMessage *clickmsg, REG(a2) APTR obj)
  2182. {
  2183.    int pos = clickmsg->ClickPosition;
  2184.    char *line = clickmsg->LineContents, *p, *surl;
  2185.    static char url[SIZE_URL];
  2186.  
  2187.    DoMethod(G->App, MUIM_Application_InputBuffered);
  2188.    while (pos && !ISpace(line[pos-1]) && line[pos-1] != '<') pos--;
  2189.    surl = &line[pos];
  2190.    for (p = url; !ISpace(line[pos]) && line[pos] != '>' && line[pos] != '\n' && line[pos] && p-url < SIZE_URL; pos++) *p++ = line[pos];
  2191.    *p = 0;
  2192.    if (RE_ExtractURL(surl, url, NULL, NULL))
  2193.       if (!strnicmp(url, "mailto:", 7)) RE_ClickedOnMessage(&url[7]);
  2194.       else GotoURL(url);
  2195.    else if (strchr(url, '@')) RE_ClickedOnMessage(url);
  2196.    else if (isdigit(line[0]) && (line[1] == ':' || line[2] == ':'))
  2197.    {
  2198.       int pnr = atoi(line), winnum;
  2199.       struct Part *part;
  2200.       get(_win(obj), MUIA_UserData, &winnum);
  2201.       part = RE_GetPart(winnum, pnr);
  2202.       RE_DecodePart(part);
  2203.       RE_DisplayMIME(part->Filename, part->ContentType);
  2204.    }
  2205.    else return FALSE;
  2206.    return TRUE;
  2207. }
  2208. MakeHook(RE_DoubleClickHook, RE_DoubleClickFunc);
  2209. ///
  2210. /// RE_ShowEnvFunc
  2211. //  Changes display options (header, textstyles, sender info)
  2212. SAVEDS ASM void RE_ShowEnvFunc(REG(a1) int *arg)
  2213. {
  2214.    int lev, winnum = arg[0], mode = arg[1];
  2215.    struct RE_ClassData *re = G->RE[winnum];
  2216.    long opt;
  2217.  
  2218.    get(re->GUI.SL_TEXT, MUIA_Prop_First, &lev);
  2219.    switch (mode)
  2220.    {
  2221.       case 0: case 1: case 2: re->Header = mode;
  2222.                               break;
  2223.       case 3: case 4: case 5: re->SenderInfo = mode-3;
  2224.                               break;
  2225.       case 6:  get(re->GUI.MI_WRAPH, MUIA_Menuitem_Checked, &opt);
  2226.                re->WrapHeader = opt; break;
  2227.       case 7:  get(re->GUI.MI_TSTYLE, MUIA_Menuitem_Checked, &opt);
  2228.                re->NoTextstyles = !opt; break;
  2229.    }
  2230.    RE_DisplayMessage(winnum);
  2231.    set(re->GUI.SL_TEXT, MUIA_Prop_First, lev);
  2232. }
  2233. MakeHook(RE_ShowEnvHook, RE_ShowEnvFunc);
  2234. ///
  2235.  
  2236. /*** GUI ***/
  2237. /// RE_LV_AttachDspFunc
  2238. //  Attachment listview display hook
  2239. SAVEDS ASM long RE_LV_AttachDspFunc(REG(a2) char **array, REG(a1) struct Part *entry)
  2240. {
  2241.    if (entry)
  2242.    {
  2243.       static char dispnu[SIZE_SMALL], dispna[SIZE_CTYPE], dispsz[SIZE_SMALL];
  2244.       array[0] = array[2] = "";
  2245.       if (entry->Nr > 0) sprintf(array[0] = dispnu, "%ld", entry->Nr);
  2246.       sprintf(array[1] = dispna, *entry->Name ? entry->Name : DescribeCT(entry->ContentType));
  2247.       if (entry->Size) sprintf(array[2] = dispsz, "%s%ld", entry->Decoded ? "" : "~", entry->Size);
  2248.    }
  2249.    return 0;
  2250. }
  2251. MakeHook(RE_LV_AttachDspFuncHook,RE_LV_AttachDspFunc);
  2252. ///
  2253. /// RE_CloseFunc
  2254. //  Closes a read window
  2255. SAVEDS ASM void RE_CloseFunc(REG(a1) int *arg)
  2256. {
  2257.    int winnum = *arg;
  2258.    struct RE_ClassData *re = G->RE[winnum];
  2259.  
  2260.    RE_CleanupMessage(winnum);
  2261.    if (Virtual(re->MailPtr))
  2262.    {
  2263.       free(re->MailPtr);
  2264.       CloseTempFile(re->TempFile);
  2265.    }
  2266.    G->Weights[2] = GetMUI(re->GUI.GR_HEAD, MUIA_VertWeight);
  2267.    G->Weights[3] = GetMUI(re->GUI.GR_BODY, MUIA_VertWeight);
  2268.    DisposeModulePush(&G->RE[winnum]);
  2269. }
  2270. MakeHook(RE_CloseHook, RE_CloseFunc);
  2271. ///
  2272. /// RE_Open
  2273. //  Opens a read window
  2274. int RE_Open(int winnum, BOOL real)
  2275. {
  2276.    if (winnum < 0) for (winnum = 0; winnum < 4; winnum++) if (!G->RE[winnum]) break;
  2277.    if (winnum > 3) return -1;
  2278.    if (!G->RE[winnum])
  2279.    {
  2280.       if (!(G->RE[winnum] = RE_New(winnum, real))) return -1;
  2281.       G->RE[winnum]->Header = C->ShowHeader;
  2282.    }
  2283.    return winnum;
  2284. }
  2285. ///
  2286. /// RE_LV_HDspFunc
  2287. //  Header listview display hook
  2288. SAVEDS ASM long RE_LV_HDspFunc(REG(a2) char **array, REG(a1) char *entry)
  2289. {
  2290.    static char hfield[40];
  2291.    char *cont = entry;
  2292.    int i = 0;
  2293.  
  2294.    clear(hfield, 40);
  2295.    while (*cont != ':' && *cont && i < 38) hfield[i++] = *cont++;
  2296.    array[0] = hfield;
  2297.    array[1] = stpblk(++cont);
  2298.    return 0;
  2299. }
  2300. MakeHook(RE_LV_HDspHook,RE_LV_HDspFunc);
  2301. ///
  2302. /// RE_New
  2303. //  Creates a read window
  2304. enum {   RMEN_EDIT=501,RMEN_MOVE,RMEN_COPY,RMEN_DELETE,RMEN_PRINT,RMEN_SAVE,RMEN_DISPLAY,RMEN_DETACH,RMEN_CROP,RMEN_NEW,RMEN_REPLY,RMEN_FORWARD,RMEN_BOUNCE,RMEN_SAVEADDR,RMEN_CHSUBJ,
  2305.          RMEN_PREV,RMEN_NEXT,RMEN_URPREV,RMEN_URNEXT,RMEN_PREVTH,RMEN_NEXTTH,
  2306.          RMEN_EXTKEY,RMEN_CHKSIG,RMEN_SAVEDEC,
  2307.          RMEN_HNONE,RMEN_HSHORT,RMEN_HFULL,RMEN_SNONE,RMEN_SDATA,RMEN_SFULL,RMEN_WRAPH, RMEN_TSTYLE };
  2308.  
  2309. APTR RE_LEDGroup(char *filename)
  2310. {
  2311.    return PageGroup, Child, HSpace(0), Child, MakeStatusFlag(filename), End;
  2312. }
  2313. struct RE_ClassData *RE_New(int winnum, BOOL real)
  2314. {
  2315.    struct RE_ClassData *data;
  2316.  
  2317.    if (data = calloc(1,sizeof(struct RE_ClassData)))
  2318.    {
  2319.       APTR tb_butt[14] = { MSG_RE_TBPrev,MSG_RE_TBNext,MSG_RE_TBPrevTh,MSG_RE_TBNextTh,MSG_Space,
  2320.                            MSG_RE_TBDisplay,MSG_RE_TBSave,MSG_RE_TBPrint,MSG_Space,
  2321.                            MSG_RE_TBDelete,MSG_RE_TBMove,MSG_RE_TBReply,MSG_RE_TBForward,NULL };
  2322.       APTR tb_help[14] = { MSG_HELP_RE_BT_PREVIOUS,MSG_HELP_RE_BT_NEXT,MSG_HELP_RE_BT_QUESTION,MSG_HELP_RE_BT_ANSWER,NULL,
  2323.                            MSG_HELP_RE_BT_DISPLAY,MSG_HELP_RE_BT_EXPORT,MSG_HELP_RE_BT_PRINT,NULL,
  2324.                            MSG_HELP_RE_BT_DELETE,MSG_HELP_RE_BT_MOVE,MSG_HELP_RE_BT_REPLY,MSG_HELP_RE_BT_FORWARD,NULL };
  2325.       int i;
  2326.       for (i = 0; i < 14; i++) SetupToolbar(&(data->GUI.TB_TOOLBAR[i]), tb_butt[i]?(tb_butt[i]==MSG_Space?"":GetStr(tb_butt[i])):NULL, tb_help[i]?GetStr(tb_help[i]):NULL, 0);
  2327.       data->GUI.SL_TEXT = ScrollbarObject, End;
  2328.       data->Header = C->ShowHeader;
  2329.       data->SenderInfo = C->ShowSenderInfo;
  2330.       data->WrapHeader = C->WrapHeader;
  2331.       data->NoTextstyles = !C->UseTextstyles;
  2332.       data->GUI.WI = WindowObject,
  2333.          MUIA_Window_Title, "",
  2334.          MUIA_HelpNode, "RE_W",
  2335.          MUIA_Window_ID, MAKE_ID('R','E','A','D'),
  2336.          MUIA_UserData, winnum,
  2337.          MUIA_Window_Menustrip, MenustripObject,
  2338.             MUIA_Family_Child, MenuObject, MUIA_Menu_Title, GetStr(MSG_Message),
  2339.                MUIA_Family_Child, data->GUI.MI_EDIT = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MEdit), MUIA_Menuitem_Shortcut,"E", MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_EDIT, End,
  2340.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MMove), MUIA_Menuitem_Shortcut, "M", MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_MOVE, End,
  2341.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MCopy),MUIA_Menuitem_Shortcut,"Y",  MUIA_UserData,RMEN_COPY, End,
  2342.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MDelete), MUIA_Menuitem_Shortcut,"Del", MUIA_Menuitem_Enabled,real, MUIA_Menuitem_CommandString,TRUE, MUIA_UserData,RMEN_DELETE, End,
  2343.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,NM_BARLABEL, End,
  2344.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_Print), MUIA_Menuitem_Shortcut, "P", MUIA_UserData,RMEN_PRINT, End,
  2345.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_Save), MUIA_Menuitem_Shortcut, "S", MUIA_UserData,RMEN_SAVE, End,
  2346.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_Attachments),
  2347.                   MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MDisplay), MUIA_Menuitem_Shortcut, "D", MUIA_UserData,RMEN_DISPLAY, End,
  2348.                   MUIA_Family_Child, data->GUI.MI_DETACH = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_SaveAll), MUIA_Menuitem_Shortcut,"A", MUIA_UserData,RMEN_DETACH, End,
  2349.                   MUIA_Family_Child, data->GUI.MI_CROP = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_Crop), MUIA_Menuitem_Shortcut,"O", MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_CROP, End,
  2350.               End,
  2351.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,NM_BARLABEL, End,
  2352.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_New), MUIA_Menuitem_Shortcut,"N", MUIA_UserData,RMEN_NEW, End,
  2353.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MReply), MUIA_Menuitem_Shortcut,"R", MUIA_UserData,RMEN_REPLY, End,
  2354.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MForward), MUIA_Menuitem_Shortcut,"W", MUIA_UserData,RMEN_FORWARD, End,
  2355.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MBounce), MUIA_Menuitem_Shortcut,"B", MUIA_UserData,RMEN_BOUNCE, End,
  2356.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,NM_BARLABEL, End,
  2357.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_MGetAddress), MUIA_Menuitem_Shortcut,"J", MUIA_UserData,RMEN_SAVEADDR, End,
  2358.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_MA_ChangeSubj), MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_CHSUBJ, End,
  2359.             End,
  2360.             MUIA_Family_Child, MenuObject, MUIA_Menu_Title, GetStr(MSG_RE_Navigation),
  2361.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MNext),  MUIA_Menuitem_Shortcut, "right", MUIA_Menuitem_Enabled,real, MUIA_Menuitem_CommandString,TRUE, MUIA_UserData,RMEN_NEXT, End,
  2362.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MPrev),  MUIA_Menuitem_Shortcut, "left", MUIA_Menuitem_Enabled,real, MUIA_Menuitem_CommandString,TRUE, MUIA_UserData,RMEN_PREV, End,
  2363.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MURNext),MUIA_Menuitem_Shortcut, "shift right", MUIA_Menuitem_Enabled,real, MUIA_Menuitem_CommandString,TRUE, MUIA_UserData,RMEN_URNEXT, End,
  2364.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MURPrev),MUIA_Menuitem_Shortcut, "shift right", MUIA_Menuitem_Enabled,real, MUIA_Menuitem_CommandString,TRUE, MUIA_UserData,RMEN_URPREV, End,
  2365.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MNextTh),MUIA_Menuitem_Shortcut, ">", MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_NEXTTH, End,
  2366.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_MPrevTh),MUIA_Menuitem_Shortcut, "<", MUIA_Menuitem_Enabled,real, MUIA_UserData,RMEN_PREVTH, End,
  2367.             End,
  2368.             MUIA_Family_Child, MenuObject, MUIA_Menu_Title, "PGP",
  2369.                MUIA_Family_Child, data->GUI.MI_EXTKEY = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_ExtractKey), MUIA_Menuitem_Shortcut,"X", MUIA_UserData,RMEN_EXTKEY, End,
  2370.                MUIA_Family_Child, data->GUI.MI_CHKSIG = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_SigCheck), MUIA_Menuitem_Shortcut,"K", MUIA_UserData,RMEN_CHKSIG, End,
  2371.                MUIA_Family_Child, data->GUI.MI_SAVEDEC = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_SaveDecrypted), MUIA_Menuitem_Shortcut,"V", MUIA_UserData,RMEN_SAVEDEC, End,
  2372.             End,
  2373.             MUIA_Family_Child, MenuObject, MUIA_Menu_Title, GetStr(MSG_MA_Settings),
  2374.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_NoHeaders),  MUIA_Menuitem_Shortcut,"0", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->Header==0, MUIA_Menuitem_Exclude,0x06, MUIA_UserData,RMEN_HNONE, End,
  2375.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_ShortHeaders), MUIA_Menuitem_Shortcut,"1", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->Header==1, MUIA_Menuitem_Exclude,0x05, MUIA_UserData,RMEN_HSHORT, End,
  2376.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_FullHeaders),  MUIA_Menuitem_Shortcut,"2", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->Header==2, MUIA_Menuitem_Exclude,0x03, MUIA_UserData,RMEN_HFULL, End,
  2377.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,NM_BARLABEL, End,
  2378.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_NoSInfo), MUIA_Menuitem_Shortcut,"3", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->SenderInfo==0, MUIA_Menuitem_Exclude,0x60, MUIA_UserData,RMEN_SNONE, End,
  2379.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_SInfo), MUIA_Menuitem_Shortcut,"4", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->SenderInfo==1, MUIA_Menuitem_Exclude,0x50, MUIA_UserData,RMEN_SDATA, End,
  2380.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_SInfoImage), MUIA_Menuitem_Shortcut,"5", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->SenderInfo==2, MUIA_Menuitem_Exclude,0x30, MUIA_UserData,RMEN_SFULL, End,
  2381.                MUIA_Family_Child, MenuitemObject, MUIA_Menuitem_Title,NM_BARLABEL, End,
  2382.                MUIA_Family_Child, data->GUI.MI_WRAPH = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_WrapHeader), MUIA_Menuitem_Shortcut,"H", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,data->WrapHeader, MUIA_Menuitem_Toggle,TRUE, MUIA_UserData,RMEN_WRAPH, End,
  2383.                MUIA_Family_Child, data->GUI.MI_TSTYLE = MenuitemObject, MUIA_Menuitem_Title,GetStr(MSG_RE_Textstyles), MUIA_Menuitem_Shortcut,"T", MUIA_Menuitem_Checkit,TRUE, MUIA_Menuitem_Checked,!data->NoTextstyles, MUIA_Menuitem_Toggle,TRUE, MUIA_UserData,RMEN_TSTYLE, End,
  2384.             End,
  2385.          End,
  2386.          WindowContents, VGroup,
  2387.             Child, (C->HideGUIElements & HIDE_TBAR) ?
  2388.                (RectangleObject, MUIA_ShowMe, FALSE, End) :
  2389.                (HGroup, GroupSpacing(0),
  2390.                   Child, HGroupV,
  2391.                      Child, data->GUI.TO_TOOLBAR = ToolbarObject,
  2392.                         MUIA_HelpNode, "RE_B",
  2393.                         MUIA_Toolbar_ImageType,      MUIV_Toolbar_ImageType_File,
  2394.                         MUIA_Toolbar_ImageNormal,    "PROGDIR:Icons/Read.toolbar",
  2395.                         MUIA_Toolbar_ImageGhost,     "PROGDIR:Icons/Read_G.toolbar",
  2396.                         MUIA_Toolbar_ImageSelect,    "PROGDIR:Icons/Read_S.toolbar",
  2397.                         MUIA_Toolbar_Description,    data->GUI.TB_TOOLBAR,
  2398.                         MUIA_Toolbar_ParseUnderscore,TRUE,
  2399.                         MUIA_Font,                   MUIV_Font_Tiny,
  2400.                         MUIA_ShortHelp, TRUE,
  2401.                      End,
  2402.                      Child, HSpace(0),
  2403.                   End,
  2404.                   Child, HSpace(8),
  2405.                   Child, VGroup,
  2406.                      Child, VSpace(0),
  2407.                      Child, HGroup,
  2408.                         TextFrame,
  2409.                         MUIA_Group_Spacing, 1,
  2410.                         MUIA_Background, MUII_TextBack,
  2411.                         Child, data->GUI.GR_STATUS[0] = PageGroup,
  2412.                            Child, HSpace(0),
  2413.                            Child, MakeStatusFlag("status_unread"),
  2414.                            Child, MakeStatusFlag("status_old"),
  2415.                            Child, MakeStatusFlag("status_forward"),
  2416.                            Child, MakeStatusFlag("status_reply"),
  2417.                            Child, MakeStatusFlag("status_waitsend"),
  2418.                            Child, MakeStatusFlag("status_error"),
  2419.                            Child, MakeStatusFlag("status_hold"),
  2420.                            Child, MakeStatusFlag("status_sent"),
  2421.                            Child, MakeStatusFlag("status_new"),
  2422.                         End,
  2423.                         Child, data->GUI.GR_STATUS[1] = RE_LEDGroup("status_crypt"),
  2424.                         Child, data->GUI.GR_STATUS[2] = RE_LEDGroup("status_signed"),
  2425.                      End,
  2426.                      Child, VSpace(0),
  2427.                   End,
  2428.                End),
  2429.             Child, VGroup,
  2430.                Child, data->GUI.GR_HEAD = HGroup, GroupSpacing(0),
  2431.                   MUIA_ShowMe, data->Header > 0,
  2432.                   MUIA_VertWeight, G->Weights[2],
  2433.                   Child, NListviewObject,
  2434.                      MUIA_NListview_NList, data->GUI.LV_HEAD = NListObject,
  2435.                         InputListFrame,
  2436.                         MUIA_NList_ConstructHook, MUIV_NList_ConstructHook_String,
  2437.                         MUIA_NList_DestructHook, MUIV_NList_DestructHook_String,
  2438.                         MUIA_NList_DisplayHook, &RE_LV_HDspHook,
  2439.                         MUIA_NList_Format, "P=\033r\0338 W=-1 MIW=-1,",
  2440.                         MUIA_NList_Input, FALSE,
  2441.                         MUIA_NList_TypeSelect, MUIV_NList_TypeSelect_Char,
  2442.                         MUIA_NList_DefaultObjectOnClick, FALSE,
  2443.                         MUIA_ContextMenu, NULL,
  2444.                         MUIA_CycleChain, 1,
  2445.                      End,
  2446.                   End,
  2447.                   Child, data->GUI.GR_INFO = ScrollgroupObject,
  2448.                      MUIA_ShowMe, FALSE,
  2449.                      MUIA_Scrollgroup_FreeHoriz, FALSE,
  2450.                      MUIA_HorizWeight, 0,
  2451.                      MUIA_Scrollgroup_Contents, data->GUI.GR_PHOTO = VGroupV, GroupSpacing(0),
  2452.                         InputListFrame,
  2453.                         Child, HVSpace,
  2454.                      End,
  2455.                   End,
  2456.                End,
  2457.                Child, data->GUI.BO_BALANCE = BalanceObject,
  2458.                   MUIA_ShowMe, data->Header > 0,
  2459.                End,
  2460.                Child, data->GUI.GR_BODY = HGroup,
  2461.                   MUIA_VertWeight, G->Weights[3],
  2462.                   MUIA_Group_Spacing, 0,
  2463.                   Child, data->GUI.TE_TEXT = NewObject(CL_TextEditor->mcc_Class,NULL,
  2464.                      InputListFrame,
  2465.                      MUIA_TextEditor_Slider, data->GUI.SL_TEXT,
  2466.                      MUIA_TextEditor_FixedFont, C->FixedFontEdit,
  2467.                      MUIA_TextEditor_DoubleClickHook, &RE_DoubleClickHook,
  2468.                      MUIA_TextEditor_ImportHook, MUIV_TextEditor_ImportHook_EMail,
  2469.                      MUIA_TextEditor_ExportHook, MUIV_TextEditor_ExportHook_Plain,
  2470.                      MUIA_TextEditor_ReadOnly, TRUE,
  2471.                      MUIA_TextEditor_ColorMap, G->EdColMap,
  2472.                      MUIA_CycleChain, TRUE,
  2473.                   End,
  2474.                   Child, data->GUI.SL_TEXT,
  2475.                End,
  2476.             End,
  2477.          End,
  2478.       End;
  2479.       if (data->GUI.WI)
  2480.       {
  2481.          DoMethod(G->App, OM_ADDMEMBER, data->GUI.WI);
  2482.          set(data->GUI.WI,MUIA_Window_DefaultObject,data->GUI.TE_TEXT);
  2483.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_EDIT            ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_EDIT,0,winnum,FALSE);
  2484.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_MOVE            ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_MoveHook,winnum);
  2485.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_COPY            ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_CopyHook,winnum);
  2486.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_DELETE          ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_DeleteHook,0,winnum,FALSE);
  2487.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_PRINT           ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_PrintHook,winnum);
  2488.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SAVE            ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_SaveHook,winnum);
  2489.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_DISPLAY         ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_DisplayHook,winnum);
  2490.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_DETACH          ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_SaveAllHook,winnum);
  2491.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_CROP            ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_RemoveAttachHook,winnum);
  2492.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_NEW             ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_NEW,0,winnum,FALSE);
  2493.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_REPLY           ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_REPLY,0,winnum,FALSE);
  2494.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_FORWARD         ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_FORWARD,0,winnum,FALSE);
  2495.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_BOUNCE          ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_BOUNCE,0,winnum,FALSE);
  2496.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SAVEADDR        ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_GetAddressHook,winnum);
  2497.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_CHSUBJ          ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_ChangeSubjectHook,winnum);
  2498.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_PREV            ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,-1,0,winnum,FALSE);
  2499.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_NEXT            ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,1,0,winnum,FALSE);
  2500.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_URPREV          ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,-1,IEQUALIFIER_LSHIFT,winnum,FALSE);
  2501.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_URNEXT          ,MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,1,IEQUALIFIER_LSHIFT,winnum,FALSE);
  2502.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_PREVTH          ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_FollowHook,-1,winnum);
  2503.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_NEXTTH          ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_FollowHook,1,winnum);
  2504.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_EXTKEY          ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_ExtractKeyHook,winnum);
  2505.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_CHKSIG          ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_CheckSignatureHook,TRUE,winnum);
  2506.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SAVEDEC         ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_SaveDecryptedHook,winnum);
  2507.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_HNONE           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,0);
  2508.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_HSHORT          ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,1);
  2509.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_HFULL           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,2);
  2510.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SNONE           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,3);
  2511.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SDATA           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,4);
  2512.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_SFULL           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,5);
  2513.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_WRAPH           ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,6);
  2514.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_MenuAction   ,RMEN_TSTYLE          ,MUIV_Notify_Application,4,MUIM_CallHook,&RE_ShowEnvHook,winnum,7);
  2515.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 0, MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime, MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,-1,MUIV_Toolbar_Qualifier,winnum,MUIV_TriggerValue);
  2516.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 1, MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime, MUIV_Notify_Application,6,MUIM_CallHook,&RE_PrevNextHook,1,MUIV_Toolbar_Qualifier,winnum,MUIV_TriggerValue);
  2517.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 2, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,4,MUIM_CallHook,&RE_FollowHook,-1,winnum);
  2518.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 3, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,4,MUIM_CallHook,&RE_FollowHook,1,winnum);
  2519.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 5, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,3,MUIM_CallHook,&RE_DisplayHook,winnum);
  2520.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 6, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,3,MUIM_CallHook,&RE_SaveHook,winnum);
  2521.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 7, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,3,MUIM_CallHook,&RE_PrintHook,winnum);
  2522.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify, 9, MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime, MUIV_Notify_Application,5,MUIM_CallHook,&RE_DeleteHook,MUIV_Toolbar_Qualifier,winnum,MUIV_TriggerValue);
  2523.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify,10, MUIV_Toolbar_Notify_Pressed,FALSE, MUIV_Notify_Application,3,MUIM_CallHook,&RE_MoveHook,winnum);
  2524.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify,11, MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime, MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_REPLY,MUIV_Toolbar_Qualifier,winnum,MUIV_TriggerValue);
  2525.          DoMethod(data->GUI.TO_TOOLBAR ,MUIM_Toolbar_Notify,12, MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime, MUIV_Notify_Application,6,MUIM_CallHook,&RE_NewHook,NEW_FORWARD,MUIV_Toolbar_Qualifier,winnum,MUIV_TriggerValue);
  2526.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_CloseRequest ,TRUE                 ,MUIV_Notify_Application,3,MUIM_CallHook,&RE_CloseHook,winnum);
  2527.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat del"        ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_DeleteHook,0,winnum,FALSE);
  2528.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat shift del"  ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_DeleteHook,IEQUALIFIER_LSHIFT,winnum,FALSE);
  2529.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat space"      ,data->GUI.TE_TEXT      ,2,MUIM_TextEditor_ARexxCmd,"Next Page");
  2530.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat backspace"  ,data->GUI.TE_TEXT      ,2,MUIM_TextEditor_ARexxCmd,"Previous Page");
  2531.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat left"       ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_PrevNextHook,-1,False,winnum);
  2532.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat right"      ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_PrevNextHook,1,False,winnum);
  2533.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat shift left" ,MUIV_Notify_Application,5,MUIM_CallHook,&RE_PrevNextHook,-1,True,winnum);
  2534.          DoMethod(data->GUI.WI         ,MUIM_Notify,MUIA_Window_InputEvent   ,"-repeat shift right",MUIV_Notify_Application,5,MUIM_CallHook,&RE_PrevNextHook,1,True,winnum);
  2535.          return data;
  2536.       }
  2537.       free(data);
  2538.    }
  2539.    return NULL;
  2540. }
  2541. ///
  2542.